贺兰兰
本帖最后由 贺兰星辰 于 2020-6-20 22:57 编辑

上一期:https://www.mcbbs.net/thread-990193-1-1.html
前言:

咕了差不多一个月,终于又打开MCBBS准备更新了,之所以想起来要更新是因为今天我看到乙烯大佬给咱发绿宝石了(再不更新对不起大家咳咳)

顺带一提:
今天我的IDEA给我推送了2020.1版本,如果可以的话,建议各位都升级到该版本,MinecraftDevelopment和Material Theme均已支持到该版本(值得一提是的,Material Theme似乎把主题和图标分开了,更新后图标会变成原版图标,要想显示Material Theme的Atom图标需要安装额外的Atom Material Icons插件)
另外,IntelliJ IDEA自2020.1版本后开始支持简体中文!只需在Markcetplace安装Chinese ?(Simplified)? Language Pack EAP这个插件即可(我试了一下,汉化还算完整,但为了确保教程质量我并不准备用)
继续上一期讲:
  当我们的插件继承(extend)org.bukkit.plugin.java.JavaPlugin(1.15.2包路径,其他版本可能有所不同,但类名应当都叫JavaPlugin)以后,插件便会又此入口开始执行插件操作。但是问题来了:
  插件怎么知道我们的主类名是啥,知道我们的插件名字叫啥?
  这就需要用到plugin.yml文件了,plugin.yml就和Forge里的mcmod.info,BungeeCord里的bungee.yml功能一样,就像一个标签的作用一样,向Bukkit传递插件主类路径,插件名,前置插件信息,作者信息,启动方式等等等等....
  在maven中,plugin.yml存于src\main\resource文件夹内(这是IDEA的设置,处于resource文件夹内的文件最后会随插件一起编译到插件根目录,而plugin.yml也确实是在插件jar的根目录运作的。

一个插件内置的plugin.yml的例子(你可能发现我是用解压缩软件打开jar的,事实上,Java的jar就是把zip改了个拓展名)

plugin.yml在IDEA中自动生成的位置(由MinecraftDevelopement插件自动生成)

接下来我们看一下plugin.yml的内容:
  1. name: Hellominecraftplugin
  2. version: ${project.version}
  3. main: cn.lingyuncraft.hellominecraftplugin.Hellominecraftplugin
  4. api-version: 1.13
复制代码
   你可能发现了plugin.yml的结构都是key: vaule这样的结构,这种结构被称为YAML结构,他们普遍都是由键值对组成的(左边那个key被称为键,右边的vaule被称为值,vaule可以重复但key在一个配置文件中只能有一个)在Bukkit中,默认的配置文件管理FileConfiguration类便是使用YAML来存取数据的。YAML结构文件的文件后缀一般为.yml。如果你开过服,你应该经常跟这种文件打交道。在以后的开发中,我们会发现Java有一种叫做Map的东西存储方式和YAML的键值对方式差不多,这我之后会提到。    接下来介绍每一个key的作用:

  1. name: Hellominecraftplugin
复制代码
指定了插件名称,插件名称会传递到logger(之后会说到)以在控制台中输出带插件前缀的报告信息。同时还可用来获取你的插件实例(高级操作,现在用不到)
  1. version: ${project.version}
复制代码
指定了插件版本,在插件主类中可通过this.getDescription().getVersion()方法获取到插件版本,可以用来检查更新啥的(Bukkit不自带检查更新,你得自己写);${project.version}是一个变量,指向了pom.xml文件(位于项目根目录)的project.version中(默认值为1.0-SNAPSHOT,SNAPSHOT是快照的意思)(还记得吗,pom.xml是Maven的配置文件),当然你也可以不用这个变量,改为自己写
  1. main: cn.lingyuncraft.hellominecraftplugin.Hellominecraftplugin
复制代码
指定了插件主类位置,Bukkit会根据此处加载插件主类,保持默认就好,没必要改
  1. api-version: 1.13
复制代码
【旧版BukkitAPI没有】指定了插件使用的API版本,然而这个键唯一的值只有1.13,而且这个配置是在1.13+的BukkitAPI才有的,以前不会有,也没必要写这个。设计这个的原因是自1.13以后,BukkitAPI出现了大改动(比如代表一个玩家的Player对象路径从org.bukkit.Player修改到了org.bukkit.entity.Player,代表物品ID的Material枚举进行了扁平化修改),导致绝大多数使用1.12.2等BukkitAPI开发的插件无法使用,当未标注api-version的插件运行于1.13+服务端时,Bukkit会给出警告。

  以上是默认生成的的plugin.yml文件,其实你还可以增加其他键值,比如:
「depend: 插件名称(即对应插件的name值)」指定了该插件依赖于某个插件(值可以是多个,格式例如depend: [A, B]),此操作会使本插件在加载时晚于指定插件加载;同时「soft-depend: 插件名称」同样支持以上操作,不同的是,如果服务端未安装depend所指定的插件,服务器会拒绝加载本插件以免引起未知错误(当然,这不是必要的,你完全可以不写这些然后手动在插件中判断是否有什么插件,甚至如果你懒你可以不写判断(就是可能会报错),这随你便)。
「author: 字符串」指定了该插件的作者,同样可以在插件主类中使用this.getDescription().getAuthors()方法获取(此方法返回了一个List<String>,d在以的教程中我们会讲述这个叫做List的东西并学习如何读取里头的东西)。
「description: "你好,Minecraft!"」指定了该插件的描述,同样可以在插件主类中使用this.getDescription().getDescription())方法获取。
其实this.getDescription()方法可以几乎获取到plugin.yml的所有内容
说了这么多,其实你会发现如果你很懒,其实你默认啥都不用改,MinecraftDevelopment已经把必要的设置给你设置好了

Java是一门“面向对象”的语言,那么什么是对象?(重难点)
  对象的本质其实就是一个类(Class),你的主类其实是一个主类对象,对象可以被“实例化”,相当于赋予他生命,实例化时往往可以通过实例化的“构造函数”对对象进行设置。
  是不是听的一头雾水?其实简单的来讲,对象是一个罐子,可以用来存东西。
  还记得第一节中,我曾经写过一个示例:
  1. public static void sendMessage(){
  2.   Bukkit.getPlayer("shaokeyibb").sendMessage("1");
  3.   Bukkit.getPlayer("shaokeyibb").sendMessage("2");
  4. }
复制代码
然而在你的主类中,onEanble()方法是这么写的:
  1. @Override
  2.     public void onEnable() {
  3.         // Plugin startup logic
  4.     }
复制代码
我们看一下他们的方法声明(也就是public开头那一行),发现什么本质上不同了吗?(先想,想好了再打开下面)

  做个假设,我们想做一个叫做Human(人类)的类,很显然人类有姓名,年龄等等属性,于是张三设计了如下Java代码:Tips:在Java中,我们使用一个等号来进行赋值操作,即A=B意为把B赋值给A,进行比较操作应该是用两个等号,如A==B,返回一个布尔值(即boolean,如果不知道boolean是什么请见下)
  1. public class Human{
  2.   //定义一个私有的,叫做age(年龄),值为16的静态int(整型)变量(下面会详细讲这类基本数据类型)
  3.   private static int age = 16;
  4.   //定义一个私有的,叫做name(姓名),值为ZhangSan的静态字符串
  5.   private static String name = "ZhangSan";
  6.   //声明一个公开的,叫做setName的,要求传入一个String的,无返回值的静态方法
  7.   public static void setName(String toName){
  8.   //将toName赋值给name
  9.   name = toName;
  10.   }
  11.   //声明一个公开的,叫做getName的,无需传参的,返回一个String的静态方法
  12.   public static String getName(){
  13.   //返回 name;
  14.   return name;
  15.   }
  16. }
复制代码
于是我们可以通直接调用getName和setName方法来设置name值的名称(值得一提的是,这两种方法通常被称为setter和getter,是很常见的开发手段,用来规范设置某变量的值而不是拿着变量直接改(在本例中,name和age都是private的,所以你无法直接修改他们,只能通过getter和setter修改,当然,本例中由于没有为age设计getter和setter所以你这辈子都别想在其他地方改他)
Tips: return关键字用于方法体中,可返回一个给定值给调用者,返回值类型必须与方法体上声明的返回值类型一致。特殊的,当声明返回值为void时,可以使用return;不返回任何东西,这么做主要是因为return以后便不会继续执行下面的语句,可用来快速结束方法调用。
调用方法获取和修改name代码如下:
  1. //此时before被赋值为“ZhangSan"
  2. String before = Human.getName();
  3. //将name设置为“WangWu”
  4. Human.setName("WangWu");
  5. //此时after被赋值为“WangWu"
  6. String after = Human.getName();
复制代码
后来你发现了奇怪的问题:这个世界又不是只有一个年龄16,名字叫张三的人,应该还有其他人类啊?于是你试着修改这些值为别人,可是你修改完了以后发现原来的变量又不见了(因为name已经被set成别的了)
出现这种问题是因为变量都是“静态的”,这就说明他们是公共的(比喻意义,不是指他是public的),任何人都可以修改这个量,很显然世界上有很多人,而不是只有一个公共的人。
这时我们就需要引入“对象”了
对象是什么?如果说静态的都是公共的,那么非静态的对象內的变量遍都是私有的,是属于这个对象的,如果需要修改这些变量,那么你改的是每个对象的变量,而不是公共变量。
示例代码:
  1. public class Human{
  2.   //声明一个值为null(空),名为name的String
  3.   String name;
  4.   //声明一个值为0,名为age的int(为何值为0请见下“基本数据类型”)
  5.   int age;
  6.   //声明Human对象构造方法,格式基本如下,声明后实例化该对象便需要提供对应参数才可实例化,同时可执行语句块內代码
  7.   public Human(String name, int age){
  8.   this.name = name;
  9.   this.age = age;
  10.   }
  11.    //声明一个公开的,叫做setName的,要求传入一个String的,无返回值的非f静态方法
  12.   public void setName(String toName){
  13.   //将toName赋值给name
  14.   name = toName;
  15.   }
  16.   //声明一个公开的,叫做getName的,无需传参的,返回一个String非静态方法
  17.   public String getName(){
  18.   //返回 name;
  19.   return name;
  20.   }
  21. }
复制代码
值得一提的是,你发现构造函数中也有一个叫做name的String,顶上也有一个,但此时两者因为并未声明在同一内部关系(顶上那个name在外面,构造函数的name在构造函数里面),所以并不冲突,但我们需要使用this.name来表示这个类上面的name,而不是里面那个(外面的那个被称为“成员变量”
通过创建对象修改和获取不同对象的name的值的代码如下:
  1. //实例化张三
  2. Human zhangSan = new Human("ZhangSan",16);
  3. //实例化王五
  4. Human wangWu = new Human("WangWu",18);
  5. //调用zhangSan的setName方法
  6. zhangSan.setName("A");
  7. //调用zhangSan的getName方法
  8. String name1 = zhangSan.getName();
  9. //调用wangWu的getName方法
  10. String name2 = wangWu.getName();
复制代码

然后动动脑子想想,name1和name2的值分应当是什么?

这便是对象的用途了
【家庭作业】
这时就有聪明的,会给咱评分回复的人就问了:那我能不能写一个静态方法,返回一个实例化对象呢?
答案是,可以,这种方法被称为“工厂方法”,通过调用静态方法返回一个实例化对象。
(Tips:一个类是可以即有静态方法又有非静态方法,此时前者可直接调用,后者需实例化对象才能调用,当然,一个对象不能调用其类中的静态方法,反过来你也不能直接调用非静态方法)



接下来我们来认识一下Java这门语言的各种基本语句和基本数据类型:先说基本数据类型
首先,String(注意大小写,在其他语言中String被写作string)代表一串字符,它其实不算基本数据类型,他其实是一个叫做java.lang.String的对象(还记得对象是什么吗,本质上对象是一个类,但是是一个被实例化的类)也就是说,事实上字符串是一个对象,他甚至可以调用一些方法(如,toLowCase()方**让字符串的所有英文字符变为小写)。声明一个字符串:
  1. String a = "Hello"; //正确,a为“Hello”
  2. String b = Hello //错误,因为Hello是一个变量,不存在一个叫做Hello的变量
  3. String c; //正确,c为null,即空值
复制代码
注意到了吗,“Hello”被双引号包围,只有这样Java才能分清这是一个“Hello”字符串而不是一个叫做“Hello”的变量。值得一提的是如果你真的想表示一个引号,而不是把字符串扩起来的那个玩意,你应该使用转义,即使用\"来表示一个",类似的,也有很多令Java难以分辨到底是字符还是标记的字符需要使用反斜杠(\)来转义。当然,还有类似于\n这样的换行符和\u0000这样的Unicode字符(关于Unicode字符,Java默认是UTF16的,这也就意味着字符串默认是支持Unicode字符(中文,特殊符号,etc.)的,所以你并没有必要把中文转成Unicode码再扔到字符串里,没这个必要)
然后,int,long,short,byte,float,double,boolean,char都是基本数据类型,在这里我只讲几个关键的,要想了解更多有关基本数据类型的信息请前往菜鸟教程—Java基本数据类型
int:代表一个整数,默认值为0(这也就是为何上面int为0而不是null的原因)
  1. int a = 3 //正确,a为3
  2. int b = "3" //错误,因为"3"是一个字符串而不是一个数字
  3. int b; //正确,b为0
复制代码
double: 代表一个浮点数(小数),默认值为0.0d(数字尾加d代表这是一个浮点数)
  1. double a = 12345.5d
  2. double b = 1.0
  3. double count = a+b //加法运算
  4. double count2 = a-b //减法运算
  5. double count3 = a*b //乘法运算
  6. double count4 = a/b
  7. //除法中除数不能为0,否则会报错;如果运算中超精度,那么超出部分会被直接删去(而不是四舍五入)
复制代码
char:代表单个字符什么是单个字符?一个字,一个字符就是单个字符
  1. char a = '@'; //这里是单引号而不是双引号
复制代码

在很多语言中,不存在String这种东西,在这些语言中表示字符串要用到char[]数组,数组表示了一堆同一个人东西,Java也支持数组,这我们以后会讲
boolean:代表是或否
boolean是个很重要的东西,他的中文名叫做布尔值,他只有两个量,即true(真)和false(假),常用来表示是否,在Java中,boolean默认值是false(注意大小写,因为在Python中,其布尔值bool的值写法为True和False)
注意,基本数据类型虽不同于对象,但他们都有着对应的对象类封装(即,包装)用来执行一些操作(如double有对应的Double对象,其中有parseDouble(String double)方法,可以将字符串内的double转为真正的double数值)
事实上,这些基本数据类型都有大小限制,即不能超出给定值,也不能定义精度,若想实现这些操作,可以尝试使用Java的大数对象  
问题来了,我能不能把int转为double,或是反过来?
答案是可以,你可以在值前使用括号进行强转(cast),在对象中,一个父类对象可以和子类对象相互强转(你可能想到了,从上到下强转可能会丢失精度或者数据,这种强转是被允许的,被叫做向下转换,相对的,还有向上转换)
  1. double b = 2.2222222;
  2. int a = (int)b; //可,a值为2,丢失了精度
  3. double c = (double)a; //可,c值为2.0,未损失精度
复制代码

进行加减法运算时,一个int值能加一个double值吗
答案依然是可以,那么我们需要强转吗,答案是不需要

  1. int a = 100;
  2. double b = 233.333333;
  3. double c = a+b //可,c值为333.333333
复制代码


那么我们不妨来实战一个向服务器说Hello的插件吧
这里我们要提到刚才说到的logger,logger是服务器用来记录服务器信息的工具,你可以在主类中使用this.getLogger()获取到这种工具,它提供了info(String info),warn(String warn),error(String err)三个方法,分别对应着“消息,警告,错误”三种消息等级,在Bukkit控制台中分别对应着白色的普通消息,黄色的警告,红色的错误三种消息形式。一般情况下我们使用this.getLogger().info()方法发送消息即可。(注意,以上info(String info)是方法的声明方式,String代表需要提供的数据类型(这里String就是指字符串),info是这个值的名称,用来向调用者简单解释这里应该填啥东西同时标识这个数据叫啥(在这里msg即message,消息的意思)。声明方法时必须提供这两样。(当然,一个方法可以请求提供多个参数,格式类似于sendMessage(Player player,String message),此时调用者需要提供两个参数才能完成调用(上面那个方法并不存在,只是我瞎编的,别去试)))
Tips:还记得调用是什么吗,调用就是执行这个方法的方法体(就是语句块,那俩大括号里头的)里的更多方法。按照上面说的,getLogger().info()方法要求提供一个String字符串,因此我们就传给他一个叫做“Hello,Server”的字符串
  1. getLogger().info("Hello,Server");
复制代码
完整代码

  1. package cn.lingyuncraft.hellominecraftplugin;

  2. import org.bukkit.plugin.java.JavaPlugin;

  3. public final class Hellominecraftplugin extends JavaPlugin {

  4.     @Override
  5.     public void onEnable() {
  6.         // Plugin startup logic
  7.         getLogger().info("Hello,Server");
  8.     }

  9.     @Override
  10.     public void onDisable() {
  11.         // Plugin shutdown logic
  12.     }
  13. }
复制代码
接下来我们需要编译插件,对于Maven来讲,编译插件十分简单,我们只需要打开IDEA右侧的“Maven”侧边栏,选择插件名称—LifeCycle—install或是直接选择Run Configiuarions內的“插件名称 Build”(相当于执行了clean和install,clean的意思是清理上次的编译代码,install就是编译当底部控制台显示BUILD SUCCESS时,你便可在项目根目录/target文件夹下找到你的插件jar(不用管前缀orginal和后缀shaded的那俩,现在用不着(如果没有就那没事))
如果底部控制台显示BUILD FAILED,那么也许你应该检查检查你的代码是否有红色错误,或者你的依赖没有自动下载完


下一节会讲:数组,API和Javadoc,List,Map,Set,for,while,switch语句,pom.xml

后记:
不知不觉的,写完这个帖子已经两点一刻了,我差不多快写了四个小时,两万多个字,算是良心了,本来说这节要讲的有的没讲拖到下一节了望大家见谅

如果可以的话,请务必给我评评分回回复,让我知道你们在看

还有,帖子里有个家庭作业记得写咳咳(可以回复到下面)

下一章: https://www.mcbbs.net/thread-1015705-1-1.html




来自群组: Server CT

LightiNike
屑贺兰终于更新了

CyghTor
u1s1,贺兰大佬的教程非常棒。然而OOP思想的掌握可不只是看看文章教程就能解决的了的。虽然大家不至于跟我一样傻,但是我在初学OOP时着实很懵,因为以前是写函数式语言嘛,所以就对对象非常懵。这些东西着实气人。个人不是很喜欢相对于现实的举例,因为这样就相当于隔了一层障碍,可能会需要先将某个东西对应到现实中的例子然后再去对应概念,这样很不好。我接下来简单说下,这些概念可能会帮助你了解OOP思想
我运用简单的术语(可能都不是术语)。大家应该都知道什么是“抽象”就是无法“具体”描述的东西,或者说是“不具体”的东西)。
“类” 就是对 “对象” 的抽象。你可能看不懂,没关系,事实上,我们认为 “范围大” 的东西一定比 “范围小” 的东西抽象 (相对而言)。
其实这就是个包括被包括的问题。A 包括 B,那么 A 就是比 B 抽象
现在可以理解“类”“对象”间的关系了吗?说白了不就是“类”范围大“对象”范围小嘛,所以才说“类” 就是对 “对象” 的抽象
先说这么多吧。到时候看贺兰更新多快,我尽量多补充,帮助大家理解。有问题或者有意见请回复。


CyghTor
还有强转那块,那应该不是自动装拆箱吧?而是隐转吧?装拆箱是包装类型和解包类型啊,值->引用 和 引用->值 那个吧?

乐之叶
帖子链接失效了

暗影月赎
伪前排支持(XD)

William_Shi
在Bukkit中,默认的配置文件管理FileConfigiuation类便是使用YAML来存取数据的。
拼写错误,是FileConfiguration
在1.15.2的spigot核心就是org.bukkit.configuration.file.FileConfiguration

Tribunny
写的非常好

2975370283
MCBBS有你更精彩~

_XuanFsant_
本帖最后由 _XuanFsant_ 于 2020-4-15 17:58 编辑

第一次到这么前,讲得很好很仔细,我这种只有一点编程基础的人也能看懂,i了i了 XD

时空掌控者
身为一个萌新,目前我对对象、类、实例化的理解是,“狗有XXXX的特点,举个例子,我家的那条白毛犬……”“有XXXX特点的狗就是类”,“举个例子”就是在进行实例化,“我家的那条白毛犬……”就是实例化之后的类——对象
我也不知道我的理解对不对

贺兰兰
时空掌控者 发表于 2020-4-21 08:52
身为一个萌新,目前我对对象、类、实例化的理解是,“狗有XXXX的特点,举个例子,我家的那条白毛犬……”“ ...

差不多,但不够严谨
按你的比喻来说,类应该是形容“有这么条狗”,但究竟是哪条,有没有我不知道;实例化是指“我按照有这么条狗的样子搞了一条狗”这个动作,对象就是指“我搞到的这条狗”

HYFan_Meng
问下这个Maven怎么添加本地的依赖啊

怜生
谢谢老板已经买了,很好用

贺兰兰
HYFan_Meng 发表于 2020-4-24 18:19
问下这个Maven怎么添加本地的依赖啊
  1. <dependency>
  2.             <groupId>com.io</groupId>
  3.             <artifactId>MythicMobs</artifactId>
  4.             <version>1.0</version>
  5.             <systemPath>${project.basedir}/lib/MythicMobsApi.jar</systemPath>
  6.             <scope>system</scope>
  7.         </dependency>
复制代码

主要是填好systemPath,groupID和artifactID,版本倒是无所谓
${project.basedir}即为项目根目录(pom.xml的所在目录)

HYFan_Meng
贺兰星辰 发表于 2020-4-25 17:10
主要是填好systemPath,groupID和artifactID,版本倒是无所谓
${project.basedir}即为项目根目录(pom.x ...

好的谢谢啦

Lidocaine
发现一点小问题
实例代码中的
getLogger.info("Hello,Server");

应该为
getLogger().info("Hello,Server");

再次感谢贺兰大佬的教程~

NameEMCC
看了几个了,还是不理解,(doge)

天不生我阿瑾
好家伙,基础内容教的这么详细,支持。

贺兰兰
上杉夏相 发表于 2020-6-2 16:45
发现一点小问题
实例代码中的
应该为

感谢指正,已修改

EvilSissi
private static String name = ZhangSan;
那边没双引号
private static String name = "ZhangSan";

万万没想到刚好下面要讲到String用法的时候
你自己都失误了

贺兰兰
All.Hail.CCC 发表于 2020-6-20 14:17
private static String name = ZhangSan;
那边没双引号
private static String name = "ZhangSan";

写的时候是纯意淫的根本没往IDE上面跑马上就去改

砚澜
贺兰老师一直再追你的贴贴

浅念ICE
怎么设置字体的颜色哇

彭应琪
66666666666666666666666

彩虹弟弟
88888888888888

2089593746
6666666666666666666666

ytddbz
良心教程值得学习