java Enum语法糖

写在前面

一直对Enum的实现原理有困惑,所以最近专门去查了查,了解到了Java的12个语法糖,决定依次学习记录一下。

Enum示例

我们先写个简单的Enum的java程序并编译一下。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public enum Week {
Monday(1),
Tuesday(2),
Wednesday(3),
Thursday(4),
Friday(5),
Saturday(6),
Sunday(7);

private final int code;

Week(int code) {
this.code = code;
}

public int getCode() {
return code;
}
}

编译程序得到class文件并用jad(下载地址)反编译该文件jad \Week.class,javap只能反编译为字节码,查看较为麻烦。
得到源码:

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
44
45
46
47
48
49
50
51
52
53
54
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
// Source File Name: Week.java


public final class Week extends Enum
{

public static Week[] values()
{
return (Week[])$VALUES.clone();
}

public static Week valueOf(String name)
{
return (Week)Enum.valueOf(Week, name);
}

private Week(String s, int i, int code)
{
super(s, i);
this.code = code;
}

public int getCode()
{
return code;
}

public static final Week Monday;
public static final Week Tuesday;
public static final Week Wednesday;
public static final Week Thursday;
public static final Week Friday;
public static final Week Saturday;
public static final Week Sunday;
private final int code;
private static final Week $VALUES[];

static
{
Monday = new Week("Monday", 0, 1);
Tuesday = new Week("Tuesday", 1, 2);
Wednesday = new Week("Wednesday", 2, 3);
Thursday = new Week("Thursday", 3, 4);
Friday = new Week("Friday", 4, 5);
Saturday = new Week("Saturday", 5, 6);
Sunday = new Week("Sunday", 6, 7);
$VALUES = (new Week[] {
Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday
});
}
}

构造函数

我们可以看到Enum其实本质还是类,所有的Enum都继承自Enum基类。
分析构造函数,我们定义的枚举常量的名字都用String类存储,并且函数会自动给常量分配一个ordinal,用来记录记录枚举常量在声明时的顺序,剩下的参数是我们在枚举中定义的参数。

super函数内容:

1
2
3
4
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}

static代码块

static代码块是对枚举类进行初始化并存入到$VALUES数组中。

values函数

返回所有的枚举常量,这个很好理解。

valueOf函数

valueOf函数通过枚举常量的名字来返回枚举常数。

函数内使用的是基类Enum中的valueOf函数,并传入Week参数,来和其他Enum类区分

1
2
3
4
5
6
7
8
9
10
public static <T extends Enum<T>> T valueOf(Class<T> enumType,
String name) {
T result = enumType.enumConstantDirectory().get(name);
if (result != null)
return result;
if (name == null)
throw new NullPointerException("Name is null");
throw new IllegalArgumentException(
"No enum constant " + enumType.getCanonicalName() + "." + name);
}

enumType.enumConstantDirectory()函数会返回所有一个包含所有枚举常量的HashMap

1
2
3
4
5
6
7
8
9
10
11
12
13
Map<String, T> enumConstantDirectory() {
if (enumConstantDirectory == null) {
T[] universe = getEnumConstantsShared();
if (universe == null)
throw new IllegalArgumentException(
getName() + " is not an enum type");
Map<String, T> m = new HashMap<>(2 * universe.length);
for (T constant : universe)
m.put(((Enum<?>)constant).name(), constant);
enumConstantDirectory = m;
}
return enumConstantDirectory;
}

一些细节

该部分摘自枚举的本质

Jvm编译器处理枚举的做了以下几件事:

  • 定义一个继承自Enum类的Fruit类,Fruit类是用final修饰的
  • 为每个枚举实例对应创建一个类对象,这些类对象是用public static final修饰的。同时生成一个数组,用于保存全部的类对象
  • 生成一个静态代码块,用于初始化类对象和类对象数组
  • 生成一个构造函数,构造函数包含自定义参数和两个默认参数
  • 生成一个静态的values()方法,用于返回所有的类对象
  • 生成一个静态的valueOf()方法,根据name参数返回对应的类实例

一些值得关注的点

  • Enum类有两个成员变量:name和ordinal。其中,name用于记录枚举常量的名字。ordinal用于记录枚举常量在声明时的顺序(从0开始)。
  • Enum类有一个构造函数,它有两个入参,分别为name和ordianl赋值。
  • Enum类重写了toString()方法,返回枚举常量的name值。
  • Enum类重写了equals()方法,直接用等号比较。
  • Enum类不允许克隆,clone()方法直接抛出异常。(保证枚举永远是单例的)
  • Enum类实现了Comparable接口,直接比较枚举常量的ordinal的值。
  • Enum类有一个静态的valueOf()方法,可以根据枚举类型以及name返回对应的枚举常量。
  • Enum类不允许反序列化,为了保证枚举永远是单例的。