2013-05-27 99 views
18

这是一个非常广泛的传播枚举单码:通过枚举方式的单例是懒惰初始化的?

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

和一堆地说,这是一个懒惰的初始化。

Java虚拟机规范给出实现的类和接口加载的时间 灵活性和链接, 但严格:一个类型的生命周期 - 但在我读的“Inside the Java Virtual Machine”第7章我很困惑定义了初始化的时间。所有实现 必须初始化每个类或接口的第一次活动使用。所述 以下六个情况资格作为活性的用途:

  • 创建类的一个新实例(在字节码,一个新的指令的执行可替换地,经由隐式的创建, 反射,克隆,或反序列化。)。
  • 由类声明的静态方法的调用(在字节码,一个invokestatic指令的执行)
  • 通过一个类或接口中声明的静态域的使用或分配,除了静态字段是最终并由 初始化一个编译时常量表达式(在字节码中S,A getstatic或putstatic指令)的执行
  • 的Java API中的某些反射的方法,如在类类方法或类在java.lang.reflect中调用 包
  • 初始化一类的子类的(一类的初始化需要其超类的现有的初始化。)
  • 一类的指定为初始类(与主()<法)时,Java虚拟机启动

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

如果我的理解错误,有人能纠正我吗? “

回答

25

enum实例字段是而不是”通过编译时常量表达式初始化“。他们 不能,因为only String and primitive types are possible types for a compile-time constant expression

这意味着当第一次访问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”,no对类A的引用。

3

具有大胆风格的第三点澄清,如果字段为“静态最后的”,现场的initialzation是发生在complie时间

不完全是 - 它仅适用于“静这是最后的和编译时初始化常量表达式“字段:

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

在你的情况下,单会被初始化d当枚举类被加载时,即第一次在您的代码中引用enumClazz

因此,它是有效的懒人,除非你有一个说法在某处你像这样的代码,例如,这将不必要地初始化你单身的类加载过程的一部分:

Class<?> c = enumClazz.class; 
+0

类辛格尔顿{公共静态最终instance = new Singleton(); ...}那么,对于泛型类的方式,这个“实例”是否被惰性初始化? –

+0

是的,它会以相同的方式懒惰地初始化。 – assylias

+0

全部清除。感谢一堆:) –