28

这是一个非常广泛的枚举单例代码:

public enum enumClazz{
   INSTANCE
   enumClazz(){
     //do something
   }
}

还有一堆地方说是懒初始化。但是在阅读了“ Java 虚拟机内部”的第 7 章——类型的生命周期后,我感到很困惑:

Java 虚拟机规范在类和接口加载和链接的时间上为实现提供了灵活性,但严格定义了初始化的时间。所有实现都必须在第一次主动使用时初始化每个类或接口。以下六种情况符合主动使用条件:

  • 创建一个类的新实例(在字节码中,新指令的执行。或者,通过隐式创建、反射、克隆或反序列化。)
  • 调用类声明的静态方法(在字节码中,调用静态指令的执行)
  • 由类或接口声明的静态字段的使用或赋值,除了最终的静态字段并由编译时常量表达式初始化(​​在字节码中,执行 getstatic 或 putstatic 指令)
  • 在 Java API 中调用某些反射方法,例如类 Class 或 java.lang.reflect 包中的类中的方法
  • 类的子类的初始化(类的初始化需要先对其超类进行初始化。)
  • 当 Java 虚拟机启动时,将一个类指定为初始类(使用 main()< 方法)

粗体的第三点说明如果字段是static final,则该字段的初始化发生在编译时。同样,INSTANCEinenumClazz隐含地等于public static final并符合第三点。

如果我的理解有误,有人可以纠正我吗?

4

2 回答 2

36

enum实例字段不是“由编译时常量表达式初始化”。它们不可能,因为只有String原始类型才是编译时常量表达式的可能类型

这意味着该类将在INSTANCE第一次访问时被初始化(这正是所需的效果)。

上面粗体文本中的例外情况存在,因为这些常量(static final使用编译时常量表达式初始化的字段)将在编译期间有效地内联:

class A {
  public static final String FOO = "foo";

  static {
    System.out.println("initializing A");
  }
}

class B {
  public static void main(String[] args) {
    System.out.println(A.FOO);
  }
}

B本例中的执行类不会初始化A(也不会打印“正在初始化 A”)。如果你查看为你生成的字节码,B你会看到一个字符串文字,其值为 "foo" 并且没有对 class 的引用A

于 2013-05-27T10:45:51.090 回答
5

粗体的第三点阐明如果该字段是'static final',则该字段的初始化发生在编译时

不完全是 - 它仅适用于“最终并由编译时常量表达式初始化的静态字段”:

static final String = "abc"; //compile time constant
static final Object = new Object(); //initialised at runtime

在您的情况下,将在加载枚举类时初始化单例,即enumClazz在您的代码中引用第一次。

所以它实际上是懒惰的,除非你的代码中的其他地方有一个使用枚举的语句。

于 2013-05-27T10:46:10.720 回答