通过enum方式的Singleton是否是懒惰初始化?

时间:2013-05-27 10:41:03

标签: java singleton

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

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

并且一堆地方说这是一个懒惰的初始化。但在阅读“Inside the Java Virtual Machine”第7章 - “类型的生命周期”:

后,我感到困惑
  

Java虚拟机规范提供了实现   类和接口加载和链接的时间灵活性,   但严格定义初始化的时间。所有实施   必须在第一次有效使用时初始化每个类或接口。该   以下六种情况有资格作为有效用途:

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

粗体风格的第三点阐明如果字段为static final,则字段的初始化在编译时发生。同样,INSTANCE中的enumClazz隐含地等于public static final并符合第三点。

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

2 个答案:

答案 0 :(得分:31)

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”,没有引用类A

答案 1 :(得分:5)

  

大胆风格的第三点澄清如果该字段是“静态最终”,则该字段的初始化发生在complie-time

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

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

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

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