![我的世界: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=1739915696-w0qxgkpdpgz9XANyvhWfCjSRJvaMYFE6-0-03c983a3385b995cc43c1f9929f7a130)
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_1.jpg?sign=1739915696-soXU5jQtCWuSVnrSSorvXsVsoFtAFPbk-0-56887eb2bf9e04cb66cc3efb96df7e54)
先从第一行代码开始:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_2.jpg?sign=1739915696-TXGU5K0HfYN2WwKfWaEYEl5XzS7ApDlO-0-4da834f3581d582e561b2bfbf5064a23)
如果与之前声明字段的代码对比,会注意到它们有一部分十分相似:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_3.jpg?sign=1739915696-z8siJXFWLKomflkG3AbKj2H7Ngar6gb8-0-a9e363d09d66a88f63d4aca10820f356)
上面两行代码声明了四个字段,并为其中三个字段提供了值。
之前的章节中曾经讲过,字段相当于为一个对象提供了一个容器,或者说提供了一个可以控制的状态。实际上,计算机本身就是一个存储并控制若干状态的设备,若干状态存储在内存中,CPU通过存取内存中的数据完成状态的切换。而程序就是用来描述状态是如何被控制的。如果状态不够用,就需要在程序中添加额外的状态,只不过在Java中,这一类额外的状态是在方法中添加的。与针对类的字段对应,我们称这类状态为变量(Variable)。
如果读者理解上面的描述有一定困难,那么只需要知道声明了一个名为entity、类型为net.minecraft.entity.Entity的变量就可以了。
实际上,Java允许编写代码时省略赋值部分,而仅声明一个变量:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_4.jpg?sign=1739915696-5aLmUiHyYJD2gG0asBAWhUFt9VtHz5hX-0-a11d30d3d4ea21288a08eb5f992c0cc1)
一个变量也可以先声明再进行赋值,比如:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_5.jpg?sign=1739915696-AcKzA72LlmJYGma1GZZ7h6PMB8xyDS6D-0-014437c392974a110b4c7046aec35140)
这与Entity entity=event.getEntity();这一行代码是等价的。
此外,多个类型相同的变量可以合并声明,如:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_6.jpg?sign=1739915696-GMRqqRZ5QllRMdY14GWmymkuE7oOGmUH-0-93ede18af2842bc1325552becb1d9f0f)
这与以下的代码是等价的:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_7.jpg?sign=1739915696-ODMmfiCM5KcsBqsu8y89UXoR1oBRE886-0-855de7e2ebcf32236d0f9925c70a4bf7)
3.4.2 条件语句
在前面的章节中已经看到了一种简单的语句,它由一个表达式加上分号组成,这里看到的语句被称为条件语句(Conditional Statement)。
条件语句的形式如下:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_57_8.jpg?sign=1739915696-M1BAtTEnsFT4X5saBLLzi2Z9UPYUo753-0-1e9fddeda4c90a7ca58a218539cd4ae4)
conditionalExpression是一个表达式,其值的类型应为boolean,就是只可能是true或false的布尔类型。当表达式的值为true时,执行括号里的所有语句,若为false,则把这些语句全部跳过。
当大括号里的语句只有一句时,大括号可以省略,换言之,下面三种写法是等价的:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_58_1.jpg?sign=1739915696-nirN3cGX7F1rBnZQ2IACcpeSuqNFROIE-0-9fde2693f4ea6ba1384eba0851a64dd8)
还有一种条件语句,在相应的表达式为true时执行第一个括号里的所有语句,否则执行第二个括号里的所有语句,它含有关键词else,具体形式如下:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_58_2.jpg?sign=1739915696-xdHZGkbASaLo6zYVE0lrcPTMxi3u3IJw-0-7767e0b2209bf267d41e4a33c8a8f893)
再比如下面这段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_58_3.jpg?sign=1739915696-6FH4cIfzCMTUDFwPQRSvNsSBJWTaXO1h-0-8c9d41b68b3d827efbacb56bfe103053)
在正常情况下,一个方法里的语句都是按顺序执行的,但是条件表达式的出现,在部分情况下,使一个方法里的部分语句被跳过,而不再被执行。我们称条件语句调整了程序的控制流(Control Flow)。
正是因为大括号内的语句只有在特殊的情况下才会执行,因此它们的地位和大括号外的语句不同,因此很多Java代码中,大括号内的语句出现时,它们的前面有若干空格,以便看清程序的结构。我们称这些空格为缩进(Indent)。实际上在为一个类添加字段和方法时,添加的字段和方法也存在缩进。作为可读性较强的程序的源代码,不同层级之间的缩进空格数应该一致,而且应该是某个整数的倍数,比如一个类的所有方法的缩进应该一致,大括号里的每一条语句的缩进也应该一致,后者的缩进空格数应该是前者的两倍。对于这个整数,不同的项目会采取不同的数值,有的是2,有的是4,有的是8,或者其他数字,对于本书,一层缩进增加的空格数是4。
大括号里可以有其他语句,当然也可以有条件语句,一种常见的方式是在else下面的语句中添加条件语句,比如下面这段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_59_1.jpg?sign=1739915696-Vz4vcW0QbIdf9k99Near7xEIDqDvNiHg-0-15d96676adbc89ca3bf84927e0f919bc)
else处的大括号可以省略,因此可以写成下面的形式:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_59_2.jpg?sign=1739915696-V5cL56PcyxiqA0e1YdDzEvzuyWNtbo9J-0-cf783d0ee82ba53450e4516c18dd68c9)
这种把else if放在一起的写法,在代码中也是相当常见的,比如下面这段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_59_3.jpg?sign=1739915696-6sWug25rQPAcFNverJh6rMx8FRmnEC0I-0-a6d746b6d27cc32da295ddd4efe12eb8)
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_1.jpg?sign=1739915696-Rw0nYXGXNz5z5t2X7GxgKVLhoZjNozbR-0-87fee6c72f0d1a9d1a1fe33d960060f0)
3.4.3 使用new运算符直接构造对象
先从条件语句的内部开始分析。
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_2.jpg?sign=1739915696-4awSuOhDmPON5jdR87rIhAe0qzrA8iir-0-665308f9ec7f269331c8fc0b0a33a35d)
声明了一个String类型的实例,调用了entity的getName方法获得了实体的名字(对玩家来说这就是玩家的游戏名),并使用了加号将这一名字和其他字符串拼接在了一起,形成了一个新的字符串。
然后使用这个字符串进行以下操作。
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_3.jpg?sign=1739915696-8EUKjL1gUoSTKHwyB2UaYkVNS5Tj7aRP-0-eb2dcf39191b094df9a2cb27eaec730e)
这里使用了new运算符。new运算符是一种用于直接构造某种类型的实例的方式,在Java代码中十分常见。
new运算符的组成形式为:
● new及随后的空格。
● 想要直接使用new运算符生成实例的类名。
● 一对小括号及其中的参数,可以有零个、一个或多个。
传入new运算符的参数是由相应的类本身决定的。打开TextComponentString类,会看到以下一段代码:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_4.jpg?sign=1739915696-Ejhhkzhdyi0HByHWyzuowgtPFbGIpIWS-0-e8317964f0cd5dfe64f17be7becfedf8)
这段代码除了定义了一个名为text的字段,还声明了一个方法,但是只要把之前编写过的方法稍加比较,就会发现不同之处:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_5.jpg?sign=1739915696-NPUY6VJH9VuHVdqNVAHI3Xme8vlT4sjQ-0-8f684b28b5b0990494deace749fa1631)
实际上,这里定义的是一个构造方法(Constructor Method)。与普通的方法不同的是,构造方法的方法名称和类名一致,同时没有声明返回值。这个构造方法的声明中有一个名为String的参数,因此它决定了当使用new运算符构造一个TextComponentString时,需要传入一个参数,而这个参数的类型也应该为String。
最后使用了entity对象的sendMessage方法将这条消息显示在聊天栏。这是一个在Mod开发中相对常用的方法。
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_60_6.jpg?sign=1739915696-Cs2nnbhxmJtCPyXkYaj75ex9M8pWbirD-0-47a59bbb374d13b1036bb3d1b6fe64d4)
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=1739915696-QKjmxUK7TycA6Qc4PS2p3JwrfqKPMf6H-0-6c97c8c56b4d91eb3ab740ffe5d5c968)
上面的表达式会在entity变量是EntityPlayer类的实例时返回true,否则为false。
现在运行客户端,然后新建并进入一个世界,读者应该能看到聊天栏出现这样的内容:
![](https://epubservercos.yuewen.com/A2CE54/17493187407064606/epubprivate/OEBPS/Images/35851_61_2.jpg?sign=1739915696-cIL3iCTSlwPjNFyaKNoJbFf2qDdnFyN0-0-d549454bbdf2b5b1bc5372ce9ffeecd4)
Player997便是启动客户端时Minecraft为作者设置的玩家名称,它可以是Player0和Player999之间的名字中的任何一个。
不过有一点似乎比较奇怪:为什么这条消息出现了两次?难道这个玩家刚刚进了两次世界?事实当然不是这样的。实际上是因为玩家在进入世界时,这个事件分别在客户端线程和服务端线程各触发了一次,所以事件总共触发了两次,因此产生了两条输出。至于客户端线程和服务端线程到底是什么,为什么开启一个客户端也会有服务端线程存在等各种问题,将放到后面的章节来讲述。