![我的世界:Minecraft模组开发指南](https://wfqqreader-1252317822.image.myqcloud.com/cover/268/32375268/b_32375268.jpg)
3.4 状态和控制
3.4.1 变量声明
以下是在上一节中编写的代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_56_2.jpg?sign=1739529451-cFOTgHVSWP9dtQKObJBbF58pZw0GA99x-0-d240412473a034f864fcce7dd4a22cdc)
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_1.jpg?sign=1739529451-RiYsUWXnFl0ItKBo98Ln8q8nP4qQLJhD-0-84bf9e10d849d83685b7e81a03d6cfdf)
先从第一行代码开始:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_2.jpg?sign=1739529451-Zx8QXXnCGRDFxqXWcnW9SV3x9cTVouAe-0-056739f0a46e5a7daab80c8354f8ed74)
如果与之前声明字段的代码对比,会注意到它们有一部分十分相似:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_3.jpg?sign=1739529451-y255czXc8Cyi1IvPIDGAmzwcnKzludJT-0-f55fd22628c3598d24b5ea163810af8e)
上面两行代码声明了四个字段,并为其中三个字段提供了值。
之前的章节中曾经讲过,字段相当于为一个对象提供了一个容器,或者说提供了一个可以控制的状态。实际上,计算机本身就是一个存储并控制若干状态的设备,若干状态存储在内存中,CPU通过存取内存中的数据完成状态的切换。而程序就是用来描述状态是如何被控制的。如果状态不够用,就需要在程序中添加额外的状态,只不过在Java中,这一类额外的状态是在方法中添加的。与针对类的字段对应,我们称这类状态为变量(Variable)。
如果读者理解上面的描述有一定困难,那么只需要知道声明了一个名为entity、类型为net.minecraft.entity.Entity的变量就可以了。
实际上,Java允许编写代码时省略赋值部分,而仅声明一个变量:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_4.jpg?sign=1739529451-xd2RZxhHToX2nkDbg67AJxWNhab6DIsJ-0-3a41d25aded4021311a94e1c3951148b)
一个变量也可以先声明再进行赋值,比如:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_5.jpg?sign=1739529451-ECjwJqtsUQlmNOQAk8xh6cLXvgEuOt3J-0-35f58e6bea4b4e933b67698cb9eb8989)
这与Entity entity=event.getEntity();这一行代码是等价的。
此外,多个类型相同的变量可以合并声明,如:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_6.jpg?sign=1739529451-pajfvdSiXo4pn5pFJVqK97OBiQcAZiFq-0-ee52d2ed74d3df9a30d35c06c5348e43)
这与以下的代码是等价的:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_7.jpg?sign=1739529451-EkfSKr5tRgy3TkfcHSndwyJeFWr3IF25-0-8bfb49d95b9b67dd1603d895e676d78c)
3.4.2 条件语句
在前面的章节中已经看到了一种简单的语句,它由一个表达式加上分号组成,这里看到的语句被称为条件语句(Conditional Statement)。
条件语句的形式如下:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_8.jpg?sign=1739529451-G81pMfC94whvv6zpUkNXsOFpkeQE4GLd-0-ce09ce6be46b5cb1495c4585996f959d)
conditionalExpression是一个表达式,其值的类型应为boolean,就是只可能是true或false的布尔类型。当表达式的值为true时,执行括号里的所有语句,若为false,则把这些语句全部跳过。
当大括号里的语句只有一句时,大括号可以省略,换言之,下面三种写法是等价的:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_58_1.jpg?sign=1739529451-sFsvYW7f9efijn0sYsirf247BemE50WL-0-b69463e60fcf2788a4b5f3861d60a973)
还有一种条件语句,在相应的表达式为true时执行第一个括号里的所有语句,否则执行第二个括号里的所有语句,它含有关键词else,具体形式如下:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_58_2.jpg?sign=1739529451-1U9AVkghje0cZHrpRrVyIuiXv26avdIC-0-a78674ec5f32758de37fa8c669acbda5)
再比如下面这段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_58_3.jpg?sign=1739529451-VtsNrGuVlxbEmf4LP7dSNjCLMfW3YSma-0-0a28a2b7473b95c3029705553db5ab89)
在正常情况下,一个方法里的语句都是按顺序执行的,但是条件表达式的出现,在部分情况下,使一个方法里的部分语句被跳过,而不再被执行。我们称条件语句调整了程序的控制流(Control Flow)。
正是因为大括号内的语句只有在特殊的情况下才会执行,因此它们的地位和大括号外的语句不同,因此很多Java代码中,大括号内的语句出现时,它们的前面有若干空格,以便看清程序的结构。我们称这些空格为缩进(Indent)。实际上在为一个类添加字段和方法时,添加的字段和方法也存在缩进。作为可读性较强的程序的源代码,不同层级之间的缩进空格数应该一致,而且应该是某个整数的倍数,比如一个类的所有方法的缩进应该一致,大括号里的每一条语句的缩进也应该一致,后者的缩进空格数应该是前者的两倍。对于这个整数,不同的项目会采取不同的数值,有的是2,有的是4,有的是8,或者其他数字,对于本书,一层缩进增加的空格数是4。
大括号里可以有其他语句,当然也可以有条件语句,一种常见的方式是在else下面的语句中添加条件语句,比如下面这段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_59_1.jpg?sign=1739529451-LAaPf6PUeg3zS2hq40sifMQ5JNyoRBfw-0-7535c541a75d06396b32d71a9aa925c3)
else处的大括号可以省略,因此可以写成下面的形式:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_59_2.jpg?sign=1739529451-yCNZKXwPRpFXvvrjhkSwxPZf2JykXVJX-0-e440a7172f91374ae4895b6058c63297)
这种把else if放在一起的写法,在代码中也是相当常见的,比如下面这段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_59_3.jpg?sign=1739529451-P9T0QunondJcJpxBgYzN2fW8zric3JuV-0-f1745ee18c64a5d742fa75c97d3e0214)
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_1.jpg?sign=1739529451-m2wUaWkCQtzbzrfP45VuQalgMEFfzSHp-0-c685026412c68102749be8ce7ae21763)
3.4.3 使用new运算符直接构造对象
先从条件语句的内部开始分析。
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_2.jpg?sign=1739529451-Qrto0X5Cr7HlBh21Q3y3ppeJbM1YCbV1-0-67a776787a1751f9583630842d959c07)
声明了一个String类型的实例,调用了entity的getName方法获得了实体的名字(对玩家来说这就是玩家的游戏名),并使用了加号将这一名字和其他字符串拼接在了一起,形成了一个新的字符串。
然后使用这个字符串进行以下操作。
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_3.jpg?sign=1739529451-c4tQsbZ9ttDxVt9pd7YWZhZ3akXWOBwo-0-843af8fbe90333722facd026690e5351)
这里使用了new运算符。new运算符是一种用于直接构造某种类型的实例的方式,在Java代码中十分常见。
new运算符的组成形式为:
● new及随后的空格。
● 想要直接使用new运算符生成实例的类名。
● 一对小括号及其中的参数,可以有零个、一个或多个。
传入new运算符的参数是由相应的类本身决定的。打开TextComponentString类,会看到以下一段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_4.jpg?sign=1739529451-FdfPDKmK8qhxNWPXImWWjUC9sukDS0vo-0-f72dcd7865960b29594c5f5deaba815b)
这段代码除了定义了一个名为text的字段,还声明了一个方法,但是只要把之前编写过的方法稍加比较,就会发现不同之处:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_5.jpg?sign=1739529451-JGt4Dm8VwsIqgPrvEJxObZg42hel56d4-0-e5cfcf8e630dc03395192e51a8e5501f)
实际上,这里定义的是一个构造方法(Constructor Method)。与普通的方法不同的是,构造方法的方法名称和类名一致,同时没有声明返回值。这个构造方法的声明中有一个名为String的参数,因此它决定了当使用new运算符构造一个TextComponentString时,需要传入一个参数,而这个参数的类型也应该为String。
最后使用了entity对象的sendMessage方法将这条消息显示在聊天栏。这是一个在Mod开发中相对常用的方法。
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_6.jpg?sign=1739529451-SMdrCDZmhG54afDTyrcRdMzKG31BHviR-0-5ea94ef58284c4fcc27f98cdc94a4452)
3.4.4 对象类型的判断
我们希望在一个实体加入世界时判断它是不是玩家,如果是就向其客户端聊天栏发送消息。代表玩家的类是net.minecraft.entity.player.EntityPlayer。换言之,希望检查的是这个Entity类的实例是否是EntityPlayer类的实例,要使用的是代码中出现的instanceof运算符:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_61_1.jpg?sign=1739529451-XbJpEUAa5hu92wP5UUSEDJsOUlOrL4Kw-0-cf68a086debe26147016cbabfa39ba1a)
上面的表达式会在entity变量是EntityPlayer类的实例时返回true,否则为false。
现在运行客户端,然后新建并进入一个世界,读者应该能看到聊天栏出现这样的内容:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_61_2.jpg?sign=1739529451-3cJMOtBQ3wRzGe9wboZ5DfxzY8yleBK4-0-6c35adea1ad8c701b85301064094b973)
Player997便是启动客户端时Minecraft为作者设置的玩家名称,它可以是Player0和Player999之间的名字中的任何一个。
不过有一点似乎比较奇怪:为什么这条消息出现了两次?难道这个玩家刚刚进了两次世界?事实当然不是这样的。实际上是因为玩家在进入世界时,这个事件分别在客户端线程和服务端线程各触发了一次,所以事件总共触发了两次,因此产生了两条输出。至于客户端线程和服务端线程到底是什么,为什么开启一个客户端也会有服务端线程存在等各种问题,将放到后面的章节来讲述。