枚举类反编译
枚举类关键字enum隐藏了很多细节,导致我们不方便理解,查阅资料后,发现反编译class文件,是一种窥探JVM运行Java代码的一种有效手段。本文大量借鉴了知乎文章 1
1  | public enum Gender {  | 
使用jad反编译工具对枚举类Gender进行分析:
1  | // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.  | 
由此可以分析出以下结论:
1  | public final class Gender extends Enum {}  | 
我们声明的枚举类是final类型的,所以无法被继承
我们声明枚举类默认都会继承自抽象类Enum,稍后会分析下Enum的源码
1  | public static final Gender MALE;  | 
声明的枚举类常量其实是一个public static final枚举类对象,所以无法被更改
$VALUES[]数组用来保存所有枚举类对象
(这里稍稍补充一下:$是由Java编译器生成的占位符,一般不推荐代码中使用)
1  | static {  | 
- 静态代码块用来初始化枚举类对象(常量),并初始化VALUES[]数组
 
1  | private Gender(String s, int i){  | 
枚举类的构造方法是私有的,它只能内部调用,所以无法在外部被初始化,即无法被new
他调用了超类的构造器,
String s是枚举类对象的值,int i是枚举类对象的ordinal的值
1  | public static Gender[] values(){  | 
- 静态的values()方法,用于返回所有的类对象,这个clone()方法是Object对象的浅拷贝方法,只会复制并返回对象的引用 2
 
1  | public static Gender valueOf(String name){  | 
- 静态的valueOf()方法,根据name参数返回对应的类对象
 
Enum源码分析
Enum类在java源码中的相对路径为:/rt.jar!/java/lang/Enum.class
1  | import java.io.Serializable;  | 
由此可以分析出以下结论:
1  | private final String name;  | 
- Enum类有两个成员变量:name和ordinal。其中,name用于记录枚举常量的名字。ordinal用于记录枚举常量在声明时的顺序(从0开始)。
 
1  | protected Enum(String name, int ordinal) {  | 
- 受保护的构造方法,反编译出来的构造器中,super调用的就是上边这个方法;
 
1  | public String toString() {  | 
Enum类重写了toString()方法,返回枚举常量的name值。
Enum类重写了equals()方法,直接用等号比较。
Enum类直接调用Object的hashCode()方法。
1  | protected final Object clone() throws CloneNotSupportedException {  | 
- Enum类不允许克隆,clone()方法直接抛出异常。(保证枚举永远是单例的)
 
1  | public final int compareTo(E o) {  | 
- Enum类实现了Comparable接口,直接比较枚举常量的ordinal的值。
 
1  | public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {  | 
- Enum类有一个静态的valueOf()方法,可以根据枚举类型以及name返回对应的枚举常量。
 
1  | private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException {  | 
- Enum类不允许反序列化,为了保证枚举永远是单例的。
 
1  | 
  | 
- 返回表示此枚举常量的枚举类型的Class对象,反射时使用。
 
Reference
1. 枚举的本质 ↩
2. Java Object clone() 方法 ↩