浅析特别的枚举类

枚举类反编译

枚举类关键字enum隐藏了很多细节,导致我们不方便理解,查阅资料后,发现反编译class文件,是一种窥探JVM运行Java代码的一种有效手段。本文大量借鉴了知乎文章 1

1
2
3
public enum Gender {
MALE, FEMALE;
}

使用jad反编译工具对枚举类Gender进行分析:

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
// 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: Gender.java


public final class Gender extends Enum{
public static Gender[] values(){
return (Gender[])$VALUES.clone();
}

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

private Gender(String s, int i){
super(s, i);
}

public static final Gender MALE;
public static final Gender FEMALE;
private static final Gender $VALUES[];

static {
MALE = new Gender("MALE", 0);
FEMALE = new Gender("FEMALE", 1);
$VALUES = (new Gender[] {
MALE, FEMALE
});
}
}

由此可以分析出以下结论:

1
public final class Gender extends Enum {}
  • 我们声明的枚举类是final类型的,所以无法被继承

  • 我们声明枚举类默认都会继承自抽象类Enum,稍后会分析下Enum的源码

1
2
3
public static final Gender MALE;
public static final Gender FEMALE;
private static final Gender $VALUES[];
  • 声明的枚举类常量其实是一个public static final枚举类对象,所以无法被更改

  • $VALUES[]数组用来保存所有枚举类对象
    (这里稍稍补充一下:$是由Java编译器生成的占位符,一般不推荐代码中使用)

1
2
3
4
5
6
7
static {
MALE = new Gender("MALE", 0);
FEMALE = new Gender("FEMALE", 1);
$VALUES = (new Gender[] {
MALE, FEMALE
});
}
  • 静态代码块用来初始化枚举类对象(常量),并初始化VALUES[]数组
1
2
3
private Gender(String s, int i){
super(s, i);
}
  • 枚举类的构造方法是私有的,它只能内部调用,所以无法在外部被初始化,即无法被new

  • 他调用了超类的构造器,String s是枚举类对象的值,int i是枚举类对象的ordinal的值

1
2
3
public static Gender[] values(){
return (Gender[])$VALUES.clone();
}
  • 静态的values()方法,用于返回所有的类对象,这个clone()方法是Object对象的浅拷贝方法,只会复制并返回对象的引用 2
1
2
3
public static Gender valueOf(String name){
return (Gender)Enum.valueOf(Gender, name);
}
  1. 静态的valueOf()方法,根据name参数返回对应的类对象

Enum源码分析

Enum类在java源码中的相对路径为:/rt.jar!/java/lang/Enum.class

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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import java.io.Serializable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;

public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {

private final String name;

public final String name() {
return name;
}

private final int ordinal;

public final int ordinal() {
return ordinal;
}

protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}

public String toString() {
return name;
}

public final boolean equals(Object other) {
return this==other;
}

public final int hashCode() {
return super.hashCode();
}

protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}

@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}

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);
}

protected final void finalize() { }

private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}

private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
}

由此可以分析出以下结论:

1
2
3
4
5
6
7
8
9
10
11
private final String name;

public final String name() {
return name;
}

private final int ordinal;

public final int ordinal() {
return ordinal;
}
  • Enum类有两个成员变量:nameordinal。其中,name用于记录枚举常量的名字。ordinal用于记录枚举常量在声明时的顺序(从0开始)。
1
2
3
4
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
  • 受保护的构造方法,反编译出来的构造器中,super调用的就是上边这个方法;
1
2
3
4
5
6
7
8
9
10
11
public String toString() {
return name;
}

public final boolean equals(Object other) {
return this==other;
}

public final int hashCode() {
return super.hashCode();
}
  • Enum类重写了toString()方法,返回枚举常量的name值。

  • Enum类重写了equals()方法,直接用等号比较。

  • Enum类直接调用Object的hashCode()方法。

1
2
3
protected final Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
  • Enum类不允许克隆,clone()方法直接抛出异常。(保证枚举永远是单例的)
1
2
3
4
5
6
7
8
public final int compareTo(E o) {
Enum<?> other = (Enum<?>)o;
Enum<E> self = this;
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
  • Enum类实现了Comparable接口,直接比较枚举常量的ordinal的值。
1
2
3
4
5
6
7
8
9
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);
}
  • Enum类有一个静态的valueOf()方法,可以根据枚举类型以及name返回对应的枚举常量。
1
2
3
4
5
6
7
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {
throw new InvalidObjectException("can't deserialize enum");
}

private void readObjectNoData() throws ObjectStreamException {
throw new InvalidObjectException("can't deserialize enum");
}
  • Enum类不允许反序列化,为了保证枚举永远是单例的。
1
2
3
4
5
6
@SuppressWarnings("unchecked")
public final Class<E> getDeclaringClass() {
Class<?> clazz = getClass();
Class<?> zuper = clazz.getSuperclass();
return (zuper == Enum.class) ? (Class<E>)clazz : (Class<E>)zuper;
}
  • 返回表示此枚举常量的枚举类型的Class对象,反射时使用。

Reference

1. 枚举的本质
2. Java Object clone() 方法