掘金 后端 ( ) • 2024-04-24 10:15

在日常开发中,经常会用到枚举类。这篇文章主要探讨一下枚举类和普通类有什么区别,以及编译过程中偷偷做了什么事情。

知识点

  • 枚举类的本质
  • 编译器对枚举类的改动

先看一段简单的枚举类代码:

 enum StatusType {

    ON(1) ,
    OFF(2);

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


    private final int code;

}

使用Compiler Explorer工具,编译成字节码

在这里插入图片描述

从上面的截图中可以看到,枚举类本质上也是一个普通类,同时继承了Enum类,而且还是final的。所以说类可以做到的事情,枚举类也可以,比如定义成员变量和成员方法。

除了自动继承了Enum类,还声明了一些静态变量和静态方法。 在这里插入图片描述

我们在枚举类定义的ONOFF对象,本质是一个静态变量。

public static final StatusType ON;

public static final StatusType OFF;

在开发中经常用到的values()和valueOf()方法,也是编译器自动帮忙生成的

private static final StatusType[] $VALUES;

public static StatusType[] values();
   0: getstatic     #10                 // Field $VALUES:[LStatusType;
   3: invokevirtual #14                 // Method "[LStatusType;".clone:()Ljava/lang/Object;
   6: checkcast     #15                 // class "[LStatusType;"
   9: areturn

 public static StatusType valueOf(java.lang.String);
       0: ldc           #4                  // class StatusType
       2: aload_0
       3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
       6: checkcast     #4                  // class StatusType
       9: areturn   

上面只是声明定义了一个静态变量,还未进行初始化,下面static块进行初始化

static {};
       0: new           #1                  // class StatusType
       3: dup
       4: ldc           #33                 // String ON
       6: iconst_0
       7: iconst_1
       8: invokespecial #34                 // Method "<init>":(Ljava/lang/String;II)V
      11: putstatic     #3                  // Field ON:LStatusType;
      14: new           #1                  // class StatusType
      17: dup
      18: ldc           #37                 // String OFF
      20: iconst_1
      21: iconst_2
      22: invokespecial #34                 // Method "<init>":(Ljava/lang/String;II)V
      25: putstatic     #7                  // Field OFF:LStatusType;
      28: invokestatic  #38                 // Method $values:()[LStatusType;
      31: putstatic     #10                 // Field $VALUES:[LStatusType;
      34: return

上面这段字节码主要就是创建了两个实例对象和一个数组,并且把两个实例对象放到数组里。不过有个点可以注意到,在调用构造函数的时候, 调用的是3个参数的构造函数,这里实际上前面两个参数是父类的构造函数需要的。

8: invokespecial #34                 // Method "<init>":(Ljava/lang/String;II)V
    /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to
     * enum type declarations.
     *
     * @param name - The name of this enum constant, which is the identifier
     *               used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned
     *         an ordinal of zero).
     */
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

总结

1、枚举类本质上也是普通类,并且是final修饰的,继承了Enum类。

2、用户定义的枚举对象转换成了静态变量对象。

3、枚举类自动添加了values()和valueOf()方法。

4、调用构造函数时,会自动调用父类的构造函数。