?> 该部分内容适合新手
如果你发现其中内容过于简单,请考虑跳过以 TR 标识的章节。
我们一步步分析,看看前面两节,你都做了哪些伟大的事情。
首先,你点击「New」、「Package」,这是在告诉 IDEA:「帮我创建一个包!」。
IDEA 当然愿意帮你,但是它需要知道你的包名(Package Name)是什么。
建立包就是在 Java 的世界中申请了一个空间,因此你需要给包提供名字。不然 Java 分不清哪个是哪个,那可就乱套了。
你输入的是:
rarityeg.helloworld
这段文字可以被看作:
<名字>.<名字>
每个「名字」都是一个包。所以这个标题应该改成「创建了两个包」,我们实际上是先创建了一个名为 rarityeg
的包,又在其中创建了一个名为 helloworld
的包。
那么包到底有什么用呢?它可以使得我们编写的类被合理地放到各个地方去,便于查找。例如,helloworld
这个包,别人一看就知道里面包含着「Hello World」的代码,如果不使用包,或者改名为 asu7fg35a435w43f
这样的名字,就乱成一团啦!
点(.
)用来分隔各个包。
类(Class)是存放信息的地方。是 Java 代码运行的地方。
你通过「New」、「Java Class」指示 IDEA 创建一个类,它为你做了,按照你输入的名字 HelloWorld
。这是类名(Class Name),类名遵循 Java 标识符规范:
- 只能由大小写字母,数字,
$
(美元符)和_
(下划线) 构成,其中数字不能用于开头 - 不能使用关键字(Keyword)
但是,为了使名字看上去易懂,我们采用帕斯卡命名法:
- 所有的单词的首字母大写,其余字母小写
这样就给类取了名字。
那到底什么是类呢?
此处内容引用自 RUNOOB Java 教程。
一个 Java 程序可以认为是一系列对象的集合,而这些对象通过调用(Call)彼此的方法来协同工作。
- 对象(Object):对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。
- 类(Class):类是一个模板,它描述一类对象的行为和状态。
- 方法(Method):方法就是行为,一个类可以有很多方法。逻辑运算、数据修改以及所有动作都是在方法中完成的。
- 实例变量(Instance Variable):每个对象都有独特的实例变量,对象的状态由这些实例变量的值决定。
Minecraft 里面有很多东西,如果把它们全都理解为对象,再按照上面的办法处理,是不是就显得很轻松了呢?
那我们这里创建的 HelloWorld
类代表的又是什么呢?
是插件本身。
HelloWorld
代表了我们这个插件,我们画出了一张蓝图(类),把插件的信息全部写在上面,然后交给了 Java 和 Bukkit。Bukkit 按照我们的蓝图,盖了个房子(对象),然后开始处理。
请记住:在 Java 中,我们只能画蓝图(定义类),这叫开发(Development),盖房子(创建对象)这样的工作我们在开发时没法做,那是 Java 在运行时(Runtime)要做的事情。
接下来我们继续往下看。
创建类时,IDEA 为我们自动补全了如下内容:
package rarityeg.helloworld;
public class HelloWorld {
}
代码是一行一行写出来的,我们也要一行一行读。
第一个是 package
语句。
?> 什么是语句?
从上一个分号开始,到下一个分号为止,是一个语句。以大括号括起来的部分作为一个整体,和其它部分放在一起,即使后面没有分号,也是一个语句。
例如:int i = 5;
是一个语句,if (a == 6){语句;语句;}
中虽然含有多个语句,但 {}
将它们括在一起,与前面的 if
等部分一起,这是一个语句。
对了!语句的最后必须有一个分号(;
),Java 用分号来识别一般语句的结束,所以一般不能省略分号,只有 }
作为语句的结尾时才可以省略分号。这种事情多写写代码就知道了
package
语句用于指定这个类属于哪个包。这就像房子在那里,你住进去后还要改写户口本一样。
接下来的 public class HelloWorld {}
整体是一个语句。这表示「定义类」,class
是用于定义类的关键字(Keyword),public
是访问修饰符,HelloWorld
是类的标识符(Identifier),大括号内是类的内容。
?> 什么是关键字?
关键字是用于实现指定功能而约定好的字符,它们有各自的功能。这里的 class
关键字的作用就是「定义一个类」,编译器看到它,就知道:「哦,要定义一个类了,应该怎么怎么样……」
?> 什么是访问修饰符?
在 Java 中,类的有些数据是只给自己用的,有些是公开的,为了保证数据安全,Java 设计了访问修饰符。public
表示「公共」,意思就是:「HelloWorld
这个类是公开的,大家随意使用哈~」
此外,你还会发现顶部的标题栏中写着 HelloWorld.java
,为什么这和类的名字一样呢?
因为这是 Java 的规范。类的名字和文件名必须相同,想想也是,如果我在家门口挂上混乱的名字,那就是撒谎造假,万万不能容忍!除此之外,一山不容二虎,一份文件中绝不能有两个公共类,不然 Java 就疯掉啦!
到目前为止,代码都是由工具生成的,下面才是我们自己输入的第一份。
改动后的代码像这样:
package rarityeg.helloworld;
import org.bukkit.plugin.java.JavaPlugin;
public class HelloWorld extends JavaPlugin {
@Override
public void onEnable() {
getLogger().info("Hello, world!");
}
}
首先大家把目光放到 import
上。还记得吗?我们说过有些代码已经由别人为我们写好了,那叫什么?对,那是轮子,import
的意思是「导入」,也就是告诉 Java:「我们现在要用这个 JavaPlugin
啦,请你帮我们找一下。」
这里我们给出的是完整的类路径。因为我们要导入的这个类并不在我们自己的空间中,Bukkit 的开发人员使用了名为 org.bukkit
的包,Bukkit 的分类工作很仔细,他们精心地将我们需要的类放在了 org.bukkit.plugin.java
下。
接下来我们来看 extends
关键字。这表示「继承」。什么是继承呢?
此处内容引用自 RUNOOB Java 教程。
「食草动物」继承了「动物」,「食肉动物」也继承了「动物」,它们都具有和「动物」类一样的方法。
继承需要符合的关系是:子类 is a 父类,父类更通用,子类更具体。
虽然食草动物和食肉动物都是属于动物,但是两者的属性和行为上有差别,所以子类会具有父类的一般特性也会具有自身的特性。
这里就相当于,JavaPlugin
是一幅蓝图,已经画好了插件的基本结构,我们把它 Copy 过来,在上面进行一些自定义的修改。我们不用操心插件是怎么被 Bukkit 加载的,因为 JavaPlugin
中已经为我们写好了。
那你应该知道后面的 JavaPlugin
是什么了吧?这又是一个标识符。为什么这里不需要写长长的 org.bukkit.plugin.java
呢?Java 很聪明,已经导入的内容,它会帮我们「记住」。这就像你去朋友家玩,第一次需要知道哪个区哪条道几单元几零几,第二次还需要那么麻烦吗?
再往下看,大家先忽略 @Override
注解,看看这个部分:
public void onEnable() {
getLogger().info("Hello, world!");
}
这就是定义了方法。那这里为什么没有像 public method onEnable
这样的 method
关键字呢?
不需要。有且仅有方法的名字后面有一对小括号 ()
,既然这样就可以识别出来,那为什么还要多一个关键字呢?浪费空间吗?所以,没有 method
关键字。
方法有时也被称为函数(Function),它们是一样的。我会尽可能按 Java 规范使用「方法」一词,但如果我说「函数」,你要知道我说的就是「方法」。
public
表示「onEnable
方法是公共的,可以被随意使用哦~」
void
是一个新词汇。这是啥东西?
这是返回值(Return Value)的类型。想想你在初中数学中学到的函数,它是不是进行了一系列计算最后得出一个结果呢?结果就叫返回值。void
表示「这个函数的返回值为空」或「这个函数不需要返回值」。如果这里不是 void
,这里应当填写返回值所属的类(返回值也是一个对象啊)。
现在你应该能够隐隐约约感受到面向对象的感觉了吧?任何东西都可以使用对象来表示,并用抽象的类来描述。
小括号内是参数(Argument),参数可以有一个,可以没有,也可以有很多。不同于返回值,这里如果没有参数,不写就可以了,没必要写一个 void
。
紧接着小括号后面的是一对大括号 {}
,大括号里面的内容就是方法体(Method Body)。方法体中含有一堆语句,当这个方法被调用时,Java 就会自动执行其中的每条语句。
?> 什么是调用?
调用(Call)表示「使用」。这个方法好不容易写好了,以后可以在各种地方使用了,使用方法就被称为调用,与此相对的,我们现在正在做的就是定义(Define)方法。
那么我们再看看大括号里面的内容:
getLogger().info("Hello, world!");
这条语句实际上可以被拆成两个部分:
getLogger()
和
<上面那个方法的返回值>.info("Hello, world!");
第一部分是一个方法调用,调用了 getLogger
方法。等一下,这个方法在哪里?我怎么没见到它?
getLogger
实际上是 JavaPlugin
的一个方法。由于我们已经继承了 JavaPlugin
,Java 会认为:「这已经是一家人了,就请随意使用吧。」于是我们就可以使用 getLogger
了。
那这为什么就表示「调用」呢?因为我们在 getLogger
后面加了一对括号 ()
。看到括号,Java 就认为:「需要的东西都准备好啦,不这个时候开始,什么时候开始?」于是就调用了这个方法。
这个方法执行后返回一个 Logger
的实例,也就是一个日志记录器。
这里再次体现了面向对象的思想。
那么我们做一个等量代换,把刚刚的第二部分:
<上面那个方法的返回值>.info("Hello, world!");
里面 <上面那个方法的返回值>
换成 <一个日志记录器>
:
<一个日志记录器>.info("Hello, world");
哦!这就很明白啦,我们使用了获得的日志记录器中的 info
方法。
?> 这个点是什么?.
在 Java 中表示「的」。把 <一个日志记录器>.info
翻译过来就是:一个日志记录器「的」info
方法。不是你的,不是我的,不是随便一个日志记录器的,而是那个日志记录器「的」。
再往下看。
info
方法后同样有一对 ()
,这也表示「调用」。那为什么 ()
里面还放进了奇怪的东西呢?
这是参数。info
方法在定义时就规定好了需要一个 String
类型的参数。也就是这个意思:「要我来帮忙?可以,但是,你要给我东西。」
info
只需要一个 String
就可以运行了。等等,哪里有 String
啊?
String
实际上是一个 Java 内置类型,简单说来就像论坛的元老们一样。内置类型和 Java 的关系很铁,Java 一直都认识它。String
用于描述一个字符串,那么按照 Java 规范,用引号 ""
夹在两侧就可以使得文字成为字符串。所以这里 "Hello, world!"
就是一个字符串。我们相当于提供了一个 String
给 info
。好啦,这样 info
就愉快地完成了交易,开始干活了。
对了!一个语句的末尾需要有分号,所以千万不要忘掉了分号哦!
现在我们回过头看 @Override
注解。这是啥子意思?
@Override
表示「覆盖」。还是举蓝图的例子。我们复制了这份蓝图,可以在上面加自己喜欢的东西。但是我突然看到蓝图上面有一点画得不好,这可怎么办啊,要是能自己来画就好了。
Java 为我们提供了这一功能,它叫重写(Override)。重写的作用就是允许子类重新定义父类的方法。
要使用重写很简单:
- 在子类中编写相同的方法,访问修饰符要兼容,返回值要一样,名字(标识符)要一样,参数的顺序和类型也要一样。(要做修改,你要告诉我改哪里啊)
- 在方法的定义的前面,加上
@Override
注解。注解可以认为是「可以自己定义的关键字」。和关键字的功能差不多。@Override
注解表示「这里要重写函数了,各位,注意一下!」
至于 Java 到底是怎么帮我们「擦掉」原来的方法,又是怎么「画上」新的方法,我们不需要在意。这项工作是 Java 来完成的。
那为什么这里我们能够改写 onEnable
呢?你可能已经知道了——定义在 JavaPlugin
里面了嘛!
plugin.yml
是一个数据文件,这也意味着它不是 Java 管理的,所以大家可以松一口气了。这种以 .yml
结尾的文件叫做 YAML 文件,设计它的目的是存储和描述数据。
YAML 像字典一样。左边是键(Key),右边是值(Value),中间用冒号和一个空格分开。
因此再来看我们的文件,不难理解了吧?
name: HelloWorld
main: rarityeg.helloworld.HelloWorld
version: 1.0
api-version: 1.16
Bukkit 在读取我们的插件时,会在这部字典里面查找 name
、main
等信息。我们需要把这些告诉 Bukkit。因此我们填写了这些数据。
main
指向的是一个类,和上面 import
时一样需要详细的路径,因为 Bukkit 并不知道我们的家在哪里,所以我们要把地址给它。
version
指定了插件的版本,一个插件发布后可能有更新,Bukkit 依照这个来判断插件是否有更新的版本。
api-version
是 Bukkit API 的版本。这主要是为了兼容而设计的。我们的插件最终可能不是在最合适的版本中运行,其它版本的 Bukkit 会根据 api-version
决定如何加载这个插件。
哇!这一节我们讲了好多东西!你学到了这些:
- 什么是包,如何创建包
- 什么是类,如何创建类
- 什么是方法,如何定义方法
- 什么是继承,如何进行继承
- 什么是调用,如何进行调用
- 什么是重写,如何进行重写
- 什么是类路径,为什么需要类路径
当然,最最重要的,还是你的成果:一个插件!
我建议你把这里的代码删掉,重新自己敲一遍,看看你有没有真正理解上面的内容。
好啦,按照我的习惯,每一章结束时,我都要放一首歌,这次也不例外,请听一听,这会给你带来不一样的感受的……
<iframe frameborder="no" border="0" marginwidth="0" marginheight="0" width="100%" height="86" src="//music.163.com/outchain/player?type=2&id=430297476&auto=0&height=66"></iframe>