通过枚举方式单例是懒惰初始化?

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

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

和一堆地方说这是一个懒惰的初始化。 但是当我阅读'Java虚拟机内部'的第7章时,我感到困惑 - 一种类型的生命周期:

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

  • 创建一个类的新实例(在字节码中,执行新的指令,或者通过隐式创建,反射,克隆或反序列化)。
  • 调用由类声明的静态方法(在字节码中,执行invokestatic指令)
  • 使用或分配由类或接口声明的静态字段,除了最终由编译时常量表达式初始化的静态字段(在字节码中,执行getstatic或putstatic指令)
  • 在Java API中调用某些反射方法,例如类Class中的方法或java.lang.reflect包中的类中的方法
  • 初始化一个类的子类(初始化一个类需要事先初始化其超类。)
  • Java虚拟机启动时将类指定为初始类(使用main()<方法)
  • 第三点以粗体显示,如果该字段是static final字段,则该字段的初始化将在编译时发生。 同样, enumClazzINSTANCE隐含地等于public static final并符合第三点。

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


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


    大胆的第三点澄清,如果该字段是“静态最终”字段的初始化发生在编译时

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

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

    在你的情况下,单例将在enum类被加载时初始化,也就是说第一次在代码中引用enumClazz

    所以它是有效的懒惰,除非你在你的代码中有这样的代码,例如,这会不必要地将你的单例初始化为类加载过程的一部分:

    Class<?> c = enumClazz.class;
    
    链接地址: http://www.djcxy.com/p/78833.html

    上一篇: Singleton via enum way is lazy initialized?

    下一篇: Singleton Class or Class with only static fields?