什么时候计算枚举变量中的函数?

When are computed the functions inside an Enum's variable?

考虑以下代码:

public enum MyOwnEnum    {

    JANVIER("janvier", "3101"),
    FEVRIER("février", new DateCalculator().getLeapYear()),
    MARS("mars", "3103"),
    AVRIL("avril", "3004");

    // Two variables, the protected constructor, getters

}

代码的行为如何?该方法是在编译时直接计算并固定,还是会在每次有人通过 MyOwnEnum.FEVRIERMyOwnEnum.valueOf("FEVRIER") 调用 FEVRIER 时计算?或者固定的,但在运行时?

枚举常量在运行时创建一次,或者更具体地说,在 class 加载时创建。

考虑以下代码:

public enum TestEnum {
    ONE("One at " + System.nanoTime()),
    TWO("Two at " + System.nanoTime());

    String value;

    TestEnum(String value) {
        System.out.println(value);
        this.value = value;
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        System.out.println("Loading");

        Class.forName("TestEnum"); // Load class

        System.out.println("Evaluating");

        System.out.println(TestEnum.ONE.value); // Evaluate value one
        System.out.println(TestEnum.TWO.value); // Evaluate value two
    }
}

这会生成以下输出:

Loading
One at 31207575500045
Two at 31207575625697
Evaluating
One at 31207575500045
Two at 31207575625697

请注意,这些值不会在后续调用中更改。

如果您查看 ENUM 生成的字节码,您会发现上面的代码与下面的代码类似:

class MyOwnEnum{
    public static MyOwnEnum JANVIER = new MyOwnEnum("janvier", "3101");
    public static MyOwnEnum FEVRIER = new MyOwnEnum(("février", new DateCalculator().getLeapYear()));
    public static MyOwnEnum MARS = new MyOwnEnum("mars", "3103");
}

这意味着您的代码将创建 DateCalculator() 的对象并调用 getLeapYear() 时它将 初始化静态变量 FEVRIER 并维护所有属性静态变量:

  1. A static variable which belongs to the class and not to object(instance)
  2. Static variables are initialized only once , at the start of the execution.
  3. Static variables will be initialized first, before the initialization of any instance variables
  4. A single copy to be shared by all instances of the class
  5. A static variable can be accessed directly by the class name and doesn’t need any object.

因为 FEVRIER 是一个静态变量 public static MyOwnEnum FEVRIER = new MyOwnEnum(("février", new DateCalculator().getLeapYear())); 这行代码将只在每个 class 加载器加载 class 时执行一次。

这里是字节码供参考:

21: ldc           #26                 // String FEVRIER
      23: iconst_1
      24: ldc           #27                 // String fΘvrier
      26: new           #29                 // class com/java8/demo/DateCalculator
      29: dup
      30: invokespecial #31                 // Method com/java8/demo/DateCalculator."<init>":()V
      33: invokevirtual #33                 // Method com/java8/demo/DateCalculator.getLeapYear:()Ljava/lang/String;
      36: invokespecial #20                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
      39: putstatic     #37                 // Field FEVRIER:Lcom/java8/demo/MyOwnEnum;
      42: new           #1                  // class com/java8/demo/MyOwnEnum
      45: dup
      46: ldc           #39                 // String MARS
      48: iconst_2
      49: ldc           #40                 // String mars
      51: ldc           #42                 // String 3103
      53: invokespecial #20                 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;Ljava/lang/String;)V
      56: putstatic     #44                 // Field MARS:Lcom/java8/demo/MyOwnEnum;
      59: new           #1                  // class com/java8/demo/MyOwnEnum