基础

  • 先来看个代码

    1
    2
    3
    4
    5
    6
    7
    package study;
    public class HelloWorld {
    public static void main(String[] args) {
    // 注:println() 后面的ln代表换行
    System.out.println("Hello, world!");
    }
    }

    这就是一般的格式了

初识

快捷键

编译器基于IDEA

快捷键 作用
alt + enter 导入包,自动修正带代码
alt + 4 运行栏显示隐藏
ctrl + y 删除光标所在行
ctrl + alt + l 格式化代码
alt + shift + 上下箭头 移动当前代码行
shift + F6 全选 (当前鼠标选中的)

数组

​ 认识上与C++相似,格式有所差异

  • 创建数组

    数组的使用还是和C++一样的

    • 动态初始化

      数据类型[] 数组名称 = new 数据类型[数组长度];

      1
      2
      3
      4
      // 只是举几个例子,代码不全
      int[] nums = new int[5];
      // 注:字符串类型数据 String 首字母要大写
      String[] strings = new String[2];
    • 静态初始化

      数据类型[] 数组名称 = new 数据类型[]{元素1, 元素2, ...};

      也可以使用省略写法

      数据类型[] 数组名称 = {元素1, 元素2, ...};

      1
      2
      String[] strings = new String[] {"Hello", "world", "java"};
      String[] strings = {"Hello", "world", "java"};

    注意事项:

    1. 静态初始化没有直接指定长度,但是仍然会自动推算得到长度
    2. 静态初始化标准格式可以拆分成为两个步骤,即定义与初始化
    3. 动态初始化也可以拆分成为两个步骤,同上
    4. 静态初始化一旦使用省略格式,就不能拆分成为两个步骤了
  • 其它

    • 动态初始化时,其中的元素将会自动拥有一个默认值 (0、null、false)
    • 直接打印数组名称,得到的是数组对应的内存地址哈希值 (不直接是内存地址)

    数组方法

    .length() 得到数组长度

    方法 (导包使用) 作用
    Arrays.toString(数组) 将数组转换为字符串
    Arrays.sort(数组) 将数组内元素按升序排序
ArrayList类

认知:数组的长度在创建时就是固定的,而通过ArrayList类创建的对象其长度可变,可以实现对数组内数据的操作,弥补这一不足,创建方法为:ArrayList<E> list = new ArrayList<>(); 其中为泛型,指数组内存放的数据类型,来看看代码:

1
2
3
4
5
6
7
8
9
10
11
12
ArrayList<String> list = new ArrayList<>();
// .add() 向集合中添加值,返回布尔值
list.add("xiao");
list.add("shuang");
System.out.println(list); // [xiao, shuang]
// .get(index) 返回对应索引的值
System.out.println(list.get(0)); // xiao
// .remove(index) 删除对应索引的值并将其返回
System.out.println(list.remove(0)); // xiao
System.out.println(list); // [shuang]
// .size() 返回集合长度
System.out.println(list.size()); // 1

常用方法:

方法(以list为例) 作用
list.add() 向集合中添加值,返回布尔值
list.get(index) 得到对应的索引值
list.remove(index) 删除对应索引的值并将其返回
list.size() 返回集合长度

注意: 为泛型,泛型只能是引用类型,不能是基本类型 ,若要使用基本类型,必须使用基本类型对应的“包装类”,见下:

1
2
3
4
5
6
7
8
9
基本类型    包装类(引用类型,包装类都位于java.lang包下,无需导入)
byte Byte
short Short
int Integer 【特殊】
long Long
float Float
double Double
char Character 【特殊】
boolean Boolean

面向对象

类的定义
1
2
3
4
5
6
7
8
9
10
11
// 来个栗子
public class ClassName {
// 成员变量
String name;
int age;
char sex;
// 成员方法
public void study() {
System.out.println("我要学习!");
}
}
对象的创建

类名称 对象名 = new 类名称()

eg. ClassName stu = new ClassName();

注意事项

  1. 与C++一样,类中也有 private 类型的变量,在类外不可调用,访问需定义一对setter/getter方法间接访问

  2. 一般类的定义单独在另一个文件中,创建对象时需导入类所在包的路径(格式:import 包名称.类名称),若对应的类与当前文件处在同一根目录下则可以省略该步骤

  3. 类中定义方法时不需要static (很容易犯错)

  4. 与C++一样,类中的方法占据单独的内存,对象中的方法存储的是对应的地址

  5. 类中的成员变量是有默认值的

  6. 当局部变量与成员变量同名时,采用”就近原则“,优先使用局部变量,可以利用this解决该冲突

Setter/Getter方法

作用:类中的成员变量一般定义为private类型(封装性),但又不可避免的需要对成员变量进行赋值等操做,除了利用构造函数 (跟C++一样)赋值外,setter方法也是常见的,而且对于一些需要判断是否合理的数据,在setter方法中可以很好的解决 (利用if-else语句),而getter方法就是对应的取值操作,下面来个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class Student {
// 成员变量
private int age;
private boolean man;
// 构造函数 (alt + lns 打开Generate面板可以快速生成这些代码)
public Student() {
}
public Student(int age, boolean man) {
this.age = age;
this.man = man;
}

// setter/getter方法是成对出现的
// setAge 这个写法是固定的,且不可有返回值
public void setAge(int x) {
if (x < 100 && x > 0) {
age = x;
} else {
System.out.println("数据不合理!");
}
}
// 不可有参数,注意返回值类型要对应
public int getAge() {
return age;
}

// 同名冲突,加上this.
public void setMan(boolean man) {
this.man = man;
}
// 特例:对于布尔值的getter函数是用is
public boolean isMan() {
return man;
}
}

小技巧

快速生成 setter/getter等方法:左上角code,点击Generate,选择 Getter and Setter,选择需要生成函数的成员变量(按住shift键可以多选),回车即可 (快捷键alt + lns 可以快速打开Generate面板)

Scanner输入对象

除了java.lang包下的内容不需要导包外,其他的包都需要import导入,该对象所在包在使用时会自动导入

认知: 通过键盘输入数据的对象,与C++中的cin作用相同,但其是对象,使用方法当然也不同,来个栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static void main(String[] args) {
// 键盘输入事件对象
Scanner sc = new Scanner(System.in);
System.out.println("请输入数据:");
// 获取键盘输入的一个int数据
// 键盘输入的其实全是字符串 (同py),该方法将字符串转化为int型数据
int age = sc.nextInt();
// 只输入sc.nextInt(),然后alt + enter 可以将代码补全为下面形式,有返回值的函数调用也可以
int i = sc.nextInt();
// 获取键盘输入的一个String数据
String name = sc.next();

// 光new不起名字为匿名对象,匿名对象可以作为函数参数和返回值
// 当确定只需要一次输入时可以直接使用匿名对象形式
String sex = new Scanner(System.in).next();

System.out.println(sex);
System.out.println(age);
System.out.println(name);
}
Random类

生成随机数的类,既然是类,使用时肯定得创建对象,来个经典的猜数字栗子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package exercise;

// 自动导包
import java.util.Random;
import java.util.Scanner;

public class 猜数字 {
public static void main(String[] args) {
// nextInt(): 无参数,取int范围内的随机数;有一个参数x,取[0, x)范围内的随机数
// Random r = new Ramdom();
// int x = r.nextInt(100) + 1;
int x = new Random().nextInt(100) + 1; // [1, 100]间的随机数
boolean active = true;
int count = 0;
// 因为需要多次猜测,就不使用匿名对象了
Scanner sc = new Scanner(System.in);
while (active) {
count += 1;
// 多次猜测,为其创建一个输入对象
int n = sc.nextInt();
if (n == x) {
System.out.println("Congratulation!");
System.out.println("You guess " + count + " times.!");
active = false;
} else if (n > x) {
System.out.println("Too big!");
} else {
System.out.println("Too small!");
}
}

if (count <= 3) {
System.out.println("买彩票吧!");
} else if (count <= 10) {
System.out.println("总算猜对了");
} else {
System.out.println("你智商余额不足,请及时充值!");
}
}
}
String字符串类

字符串都已经很熟悉了,来看看下面的代码:

1
2
3
4
String s1 = new Scanner(System.in).next(); // 输入Hello
String s2 = "Hello";
System.out.println(s1 == s2); // false
System.out.println(s2.equals(s1)); // true

将一个输入的字符串与一样但是是直接定义的字符串相比较时,居然是false,这就有问题了

  • 字符串的比较

    了解:对于基本类型来说,’==’是进行数值的比较,而对于引用类型来说,’==’是进行【地址值】的比较,另外,所有new出来的对象都是存储在堆中,而直接定义的字符串 (就是写出来了的)是存储在字符串常量池中。即使两字符串完全相同,但创建方式致使其地址不同,所以直接比较返回的也是false

    想要直接比较两字符串的值,可以使用两个方法:

    .equals() 比较字符串本身而不是地址

    .equalsIgnoreCase() 比较字符串时不区分大小写

    注意:在比较时,若有常量,推荐将常量写在前面,避免空指针异常 (变量若是null,调用比较方法会出现空指针异常错误)

  • 常用方法

    方法 作用
    .length() 得到字符串的长度
    .concat(String) 拼接字符串 (原来的字符串不受影响)
    .charAt(index) 获取指定索引位置的单个字符
    .indexOf(String) 查找参数字符串在字符串中首次出现的位置,若没有,返回-1
    .substring(index) 截取字符串 [index, end),包括指定位置的字符
    .substring(begin, end) 截取字符串 [begin, end)
    .toCharArray() 将字符串转换为字符数组形式
    .getBytes() 将字符串转换为字节数组形式
    .replace(String1, String2) 将字符串中的string1全部替换为string2

    有点多哈,不过确实都是常用的,还有个方法:

    .split(String) 切割字符串,按照参数的规则将字符串切割为若干部分,返回字符串数组

    区分:切割不同于截取,指定切割的字符会除掉

    1
    2
    3
    4
    5
    6
    String str = "xiao xiao shuang";
    String[] array = str.split("i");
    // array.fori 快捷写法
    for (int i = 0; i < array.length; i++) {
    System.out.println(array[i]);
    }

    结果为:x

              ao x
              ao shuang

    显然,”i“已经没了

Math数学工具类

了解几个常见方法即可

方法 作用
Math.abs(double num) 获取绝对值
Math.ceil(double num) 向上取整数
Math.floor(double num) 向下取整数
Math.round(double num) 四舍五入

另外,Math.PI 代表的是圆周率常量

继承

java语言中是单继承的,即一个子类不能有两个父类

  • 格式

    1
    2
    3
    public class 子类名称 extends 父类名称 {
    // ...
    }
  • 重名变量的访问

    对于类中的方法访问成员变量,若父子类中有相同变量名,方法属于谁,就优先用谁的变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    // 父类
    public class Fu {
    int num = 10;
    }

    // 子类
    public class Zi extends Fu {
    int num = 20;
    public void method() {
    int num = 30;
    System.out.println(num); // 30
    System.out.println(this.num); // 20
    // 不仅变量,父类中的同名方法也可以利用 super. 调用
    System.out.println(super.num); // 10
    }
    }

    分析:在方法中,根据就近原则,直接访问num为局部变量;this.num代表的该类中的成员变量;super.num代表着父类中的成员变量

  • 方法覆盖

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 父类
    public class Fu {
    public void method() {
    // ...
    }
    }

    // 子类
    public class Zi extends Fu {
    @Override
    public void method() {
    // ...
    }
    }

    大体与C++相同,就不写详细了,有几点可以了解一下:

    1. @Override:写在方法前面,可以检测是否为正确的覆盖重写 (可不写,但为了避免小手一抖出错,建议加上)
    2. 子类方法的返回值必须【小于等于】父类方法的返回值范围 (比如:Object > String)
    3. 子类方法的权限必须【大于等于】父类方法的权限修饰符 (public > protected > (default) > private)
  • 构造函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 父类
    public class Fu {
    public Fu(int x) {
    System.out.println("父类构造函数调用");
    }
    }

    // 子类
    public class Zi extends Fu {
    public Zi() {
    super(num); // 这必须写在前面哈
    System.out.println("子类构造函数调用");
    }
    }

    创建子类对象时,肯定是先调用父类的构造函数,父类构造函数利用super()调用,有两种情况:

    1. 父类构造函数为无参数的,子类中可以不写,默认会自带super();
    2. 父类构造函数包含参数的,子类中必须写,super(对应参数);
  • 抽象类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 抽象类,可以理解为C++中的虚基类
    public abstract class Animal {
    // 抽象方法,可以理解为C++中的纯虚函数
    public abstract void eat();
    // 普通方法
    public void normal() {
    // ...
    }
    }

    注意:

    1.抽象类不可创建对象,需要声明 abstract,且抽象方法必须在抽象类中;

    2.继承抽象类的子类必须覆盖重写抽象类中的抽象方法

多态
  • 多态的体现

    1. 父类引用指向子类对象 父类名称 对象名 = new 子类名称();

    2. 接口应用指向实现类对象 接口名称 对象名 = new 实现类名称();

    老实说,我觉得这很像C++中的虚函数,动态关联 (解决疑惑)

    当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法

  • 成员变量/ 方法的使用

    使用多态方式创建出来对象obj,要区分 obj.numobj.sum(),以 Fu obj = new Zi(); 为例:

    前提:父、子类中均含有 numsum()

    obj.num:访问成员变量,优先用 Fu 类中的成员变量 (以等号左边优先)

    obj.sum():访问成员方法,看new出来的是谁,优先使用谁的成员方法 (以等号右边优先)

  • 转型

    分为两种:

    1. 向上转型:就是多态写法

    2. 向下转型:是一个还原动作,将向上转型的对象还原

      格式:子类名称 对象名 = (子类名称) 父类对象

      注:该父类对象必须是父类名称 对象名 = new 子类名称()这样定义的

    关键字instanceof:判断父类引用是否为对应的对象,返回布尔值

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void main(String[] args) {
    // Animal 为Cat 和 Dog 的父类
    Animal animal = new Cat();
    // 判断 animal 本来是不是 Cat
    if (animal instanceof Cat) {
    Cat cat = (Cat) animal;
    }
    // 判断 animal 本来是不是 Dog
    if (animal instanceof Dog) {
    Dog dog = (Dog) animal;
    }
    }

    这样看不出instanceof的意义,但在参数传递时,若形参为Animal animal,实参为Cat 或 Dog,那在函数体中调用子类对象特有成员时,就必须清楚到底如何向下转型,这时,instanceof就很有必要了

接口

接口是最高级的抽象类,没有构造函数

首先得清楚:为什么要使用接口 ?

接口的定义
1
2
3
4
public interface 接口名称 {
// 抽象方法
// ...
}

与类的定义很相似,只是将 class 变成了 interface,另外,既然接口是抽象类,那其中定义的方法也应该是抽象方法,实际上也默认是抽象方法,即 public abstract是可以省略的

实现类

接口需要具体的类去实现 (继承),该类就叫“实现类”,其写法如下:

1
2
3
4
public class 类名称 implements 接口名称 {
// 抽象方法的覆盖重写
// ...
}

实现类必须对接口中的抽象方法全部进行覆盖重写,除非实现类是抽象类

默认方法/ 静态方法

前面也说了,接口中定义的应该都是抽象方法,但随着java语言的不断完善,在接口中也可以定义默认/ 静态方法,默认/ 静态方法有函数体

1
2
3
4
5
6
7
8
9
// 定义默认方法
public default void 方法名() {
// 函数体
}

// 定义静态方法
public static void 方法名() {
// 函数体
}

虽然都是在接口中定义的,但使用起来可不一样:

  1. 默认方法会被实现类继承,可通过实现类的对象调用,也可以覆盖重写

  2. 静态方法不能通过实现类的对象调用,而是直接通过接口调用,即:接口名.静态方法

私有方法

有时接口中的默认/ 静态方法中包含许多相同代码块,为了减少重复代码,可以将相同的代码抽取出来放在新的函数中,直接调用即可,但这个新函数是不希望可以通过同实现类或者接口名调用的,所以就有了私有方法:

1
2
3
4
5
6
7
8
9
// 默认私有方法
private void 方法名() {
// 方法体
}

// 静态静态方法
private static void 方法名() {
// 方法体
}

私有方法只允许在接口中调用

成员变量

接口中的成员变量相当于常量,定义时必须赋值,且赋值后不可修改

声明形式:public static final 数据类型 常量名称 = 数据值;

注意:

  1. public static final是默认的,可以省略

  2. 既然有 static,那常量调用的形式就与静态方法一样,通过接口名调用

  3. 常量名一般使用全大写字母,可以有下划线

父类与接口
  • 继承父类与多接口

    接口是可以与继承一起使用的,前面已经说到,java语言中是单继承,子类不能有两个父类,但接口可以有多个

    1
    2
    3
    4
    public class Zi extends Fu implements InterfaceA, InterfaceB {
    // 覆盖重写接口中所有抽象方法
    // ...
    }

    注意事项:

    1. 若实现类所实现的多个接口中,存在重复的抽象方法,则只需要覆盖重写一次即可
    2. 若实现类所实现的多个接口中,存在重复的默认方法,那么实现类一定要对冲突的默认方法进行覆盖重写
    3. 若一个类所继承父类中的方法,与接口当中的默认方法产生了冲突,优先使用父类中的方法
  • 接口之间的多继承

    接口之间也可以继承,而且可以多继承

    1
    2
    3
    public interface MyInterface extends InterfaceA, InterfaceB {
    // ...
    }

    注意事项:

    1. 若多个父接口中的抽象方法重复,只会继承一次 (抽象方法没有函数体,不存在冲突)

    2. 若多个父接口中的默认方法重复,则子接口中必须覆盖重写

final关键字

代表最终、不可改变的

  • 修饰类

    1
    2
    3
    public final class 类名称 {
    // ...
    }

    使用final修饰的类不能有任何子类 (字面意思,最后的类)

  • 修饰方法

    1
    2
    3
    public final void method() {
    // ...
    }

    使用final修饰的方法为最终方法,即不可被覆盖重写,子类中也不行

    注意:类与方法中abstractfinal是不能同时使用的,两者自相矛盾

  • 修饰局部变量

    在普通定义的成员变量前加上final,代表该局部变量不可再改变

    注:对于基本类型来说,不可再改变为其中的数据;而对于应用类型来说,不可再改变为其中的地址值

    String为例:

    1
    2
    final String name = new String();
    name = new String(); // 报错

    上面的代码就是错误的,name被声明为final类型,其地址值不可二次赋值 (可以理解为C++中 指向对象的常指针)

  • 修饰成员变量

    也是在普通定义的成员变量前加上final,代表该成员变量不可再改变

    与局部变量的区别:

    1. 成员变量是定义在类中的,会有默认值,所以被声明为final类型的成员变量必须手动初始化
    2. 手动初始化有两种方法:1.> 直接在后面写”= …“; 2.> 在所有的构造方法中都进行赋值

四种权限修饰符

Java中有四种权限修饰符访问关系:

​ public > protected > (default) > private
同一个类(自己) YES YES YES YES
同一个包(邻居) YES YES YES NO
不同包子类(儿子) YES YES NO NO
不同包非子类(陌生人) YES NO NO NO

注意事项:(default)并不是关键字“default”,而是根本不写

内部类

字面意思,在类的内部包含另一个类

  • 成员内部类

    1
    2
    3
    4
    5
    6
    7
    // 在类中直接包含另一个类称为成员内部类:内部类为外部类的一个成员
    public class 外部类名称 {
    public class 内部类名称 {
    // ...
    }
    // ...
    }

    注意:内部类可以随意访问外部类中的成员,而外部类需借助内部类对象才能访问内部类成员

    使用方法:

    1. 间接使用:在外部类的方法中,使用内部类创建对象并调用内部类中的方法,然后在main函数中只调用外部类中的方法;

    2. 直接使用:直接在main函数中创建内部类对象,再通过内部类对象调用其方法,但创建对象需按照一定的格式

      外部类名称.内部类名称 对象名 = new 外部类名称().new 内部类名称();

    再来看看熟悉的重名问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    public class Outer {
    int num = 10;
    public class Inner {
    // 内部类中的成员变量
    int num = 20;
    public void method() {
    // 内部类中的局部变量
    int num = 30;
    System.out.println(num); // 30
    System.out.println(this.num); // 20
    System.out.println(Outer.this.num); // 10
    }
    }
    // ...
    }

    分析:直接打印num,其值还是遵循就近原则;然后this.num,指该方法所在类中的num;若想访问外部类中的成员变量,就得强调为Outer.this.num

  • 局部内部类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 在类中的方法中包含另一个类称为局部内部类
    public class 外部类名称 {
    public void method() {
    class 内部类名称 {
    // ...
    }
    }
    // ...
    }

    局部内部类只能在其所在的方法中使用。

1
2
3
4
5
6
7
8
9
10
11
public class Outer {
public void method() {
final int num = 10;
class Inner {
public void getNum() {
System.out.println(num);
}
}
}
// ...
}

若局部内部类希望访问所在方法中的局部变量,那这个局部变量必须是有效final,与生命周期有关,不详细说明了

  • 匿名内部类

    如果接口的实现类(或者是父类的子类)只需要使用唯一的一次,那么这种情况下就可以省略掉该类的定义,而改为使用【匿名内部类】。

    格式:接口名称 对象名 = new 接口名称() {} {…}是匿名内部类的内容

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    public static void main(String[] args) {
    // 使用匿名内部类,但不是匿名对象,对象名称就叫objA
    // 此处 MyInterface 为接口名称,注意:new 的是接口,区别下前面所说的的"接口不能 new"
    MyInterface objA = new MyInterface() {
    @Override
    public void method1() {
    System.out.println("匿名内部类实现了方法!111-A");
    }

    @Override
    public void method2() {
    System.out.println("匿名内部类实现了方法!222-A");
    }
    };
    objA.method1();
    objA.method2();
    System.out.println("=================");

    // 使用了匿名内部类,而且省略了对象名称,也是匿名对象
    new MyInterface() {
    @Override
    public void method1() {
    System.out.println("匿名内部类实现了方法!111-B");
    }

    @Override
    public void method2() {
    System.out.println("匿名内部类实现了方法!222-B");
    }
    }.method1();
    // 因为匿名对象无法调用第二次方法,所以需要再创建一个匿名内部类的匿名对象
    new MyInterface() {
    @Override
    public void method1() {
    System.out.println("匿名内部类实现了方法!111-B");
    }

    @Override
    public void method2() {
    System.out.println("匿名内部类实现了方法!222-B");
    }
    }.method2();
    }

    区分

    1. 匿名内部类,在【创建对象】的时候,只能使用唯一一次。
    2. 匿名对象,在【调用方法】的时候,只能调用唯一一次。
    3. 匿名内部类是省略了【实现类/子类名称】,但是匿名对象是省略了【对象名称】

注意事项:

  1. 外部类的修饰符只能为public/ (default)
  2. 成员内部类的修饰符可以随意
  3. 局部内部类不能有修饰符

特殊的成员变量

  • 类作为成员变量

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Weapon {
    private String code;

    // 构造函数和 Getter/ Setter 此处省略
    }

    public class Hero {
    private String name;
    private int age;
    // 自定义的类 Weapon
    private Weapon weapon;

    public void attack() {
    System.out.println(age + "岁的" + name + "使用" + weapon.getCode() + "打怪兽");
    }
    // 构造函数和 Getter/ Setter 此处省略
    }

    String相似,自己定义的类也是可以作为成员变量的,只是赋值时应是: (以类Hero的对象hero为例)

    hero.setWeapon(new Waepon().setCode("太极剑")) 使用实名对象也可以

  • 接口作为成员变量

    与上述的类是相似的,就不过多描述了