封面作者:千夜QYS3
兜兜转转还是决定恶补基础了,文章参考廖雪峰的java教程

枚举类

使用以及好处

  • 使用
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    // enum
    public class Main {
    public static void main(String[] args) {
    Weekday day = Weekday.SUN;
    if (day == Weekday.SAT || day == Weekday.SUN) {
    System.out.println("Work at home!");
    } else {
    System.out.println("Work at office!");
    }
    }
    }

    enum Weekday {
    SUN, MON, TUE, WED, THU, FRI, SAT;
    }
  • 好处:
    • enum常量本身带有类型信息,即Weekday.SUN类型是Weekday,编译器会自动检查出类型错误
    • 不可能引用到非枚举的值,因为无法通过编译。
    • 不同类型的枚举不能互相比较或者赋值,因为类型不符。

enum的比较

两种方法,第一种更直接也更方便

1
2
3
4
if (day == Weekday.FRI) { // ok!
}
if (day.equals(Weekday.SUN)) { // ok, but more code!
}

enum类型

引言

enum就是class,但是有以下几个特点:

  • 定义的enum类型总是继承自java.lang.Enum,且无法被继承;
  • 只能定义出enum的实例,而无法通过new操作符创建enum的实例;
  • 定义的每个实例都是引用类型的唯一实例;
  • 可以将enum类型用于switch语句。

枚举实例的一些方法

name()

返回常量名,例如:

1
String s = Weekday.SUN.name(); // "SUN"

ordinal()

  1. 返回定义的常量的顺序,从0开始计数,例如:
    1
    int n = Weekday.MON.ordinal(); // 1
  2. 改变枚举常量定义的顺序就会导致ordinal()返回值发生变化。例如:
    1
    2
    3
    public enum Weekday {
    SUN, MON, TUE, WED, THU, FRI, SAT;
    }//这时候再同样输出就不是1了
    所以如果在代码中编写了类似if(x.ordinal()==1)这样的语句,就要保证enum的枚举顺序不能变。新增的常量必须放在最后。
  3. 不要依靠ordinal()的返回值,不小心修改了枚举的顺序编译器无法检查此类错误

    所以可以给每个枚举常量添加字段

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    // enum
    public class Main {
    public static void main(String[] args) {
    Weekday day = Weekday.SUN;
    if (day.dayValue == 6 || day.dayValue == 0) {
    System.out.println("Work at home!");
    } else {
    System.out.println("Work at office!");
    }
    }
    }

    enum Weekday {
    MON(1), TUE(2), WED(3), THU(4), FRI(5), SAT(6), SUN(0);

    public final int dayValue;

    private Weekday(int dayValue) {
    this.dayValue = dayValue;
    }
    }

    • 注意:枚举类的字段也可以是非final类型,即可以在运行期修改,但是不推荐这样做!
  4. 判断枚举常量的名字,要始终使用name()方法,绝不能调用toString()!
    • 默认情况下,对枚举常量调用toString()会返回和name()一样的字符串。但是,toString()可以被覆写,而name()则不行。

switch

  • 枚举类可以应用在switch语句中。因为枚举类天生具有类型信息和有限个枚举常量,所以比int、String类型更适合用在switch语句中:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    // switch
    public class Main {
    public static void main(String[] args) {
    Weekday day = Weekday.SUN;
    switch(day) {
    case MON:
    case TUE:
    case WED:
    case THU:
    case FRI:
    System.out.println("Today is " + day + ". Work at office!");
    break;
    case SAT:
    case SUN:
    System.out.println("Today is " + day + ". Work at home!");
    break;
    default:
    throw new RuntimeException("cannot process " + day);
    }
    }
    }
    enum Weekday {
    MON, TUE, WED, THU, FRI, SAT, SUN;
    }

    加上default语句,可以在漏写某个枚举常量时自动报错,从而及时发现错误。

记录类

使用record关键字

1
record Point(int x, int y) {}

等同于

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
final class Point extends Record {
private final int x;
private final int y;

public Point(int x, int y) {
this.x = x;
this.y = y;
}

public int x() {
return this.x;
}

public int y() {
return this.y;
}

public String toString() {
return String.format("Point[x=%s, y=%s]", x, y);
}

public boolean equals(Object o) {
...
}
public int hashCode() {
...
}
}

除了用final修饰class以及每个字段外,编译器还自动为我们创建了构造方法,和字段名同名的方法,以及覆写toString()equals()hashCode()方法。
换句话说,使用record关键字,可以一行写出一个不变类。
enum类似,我们自己不能直接从Record派生,只能通过record关键字由编译器实现继承。

构造方法

  • 假设Point类的xy不允许负数,我们就得给Point的构造方法加上检查逻辑:
    1
    2
    3
    4
    5
    6
    7
    8
    public record Point(int x, int y) {
    public Point {
    if (x < 0 || y < 0) {
    throw new IllegalArgumentException();
    }
    }
    }

  • 作为recordPoint仍然可以添加静态方法。一种常用的静态方法是of()方法,用来创建Point
    1
    2
    3
    4
    5
    6
    7
    8
    public record Point(int x, int y) {
    public static Point of() {
    return new Point(0, 0);
    }
    public static Point of(int x, int y) {
    return new Point(x, y);
    }
    }