通过enum方式的Singleton是否是懒惰初始化?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过enum方式的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
并且符合第三点。
如果我的理解错了,有人可以纠正我吗?
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
。
粗体风格的第三点阐明如果字段是“静态最终”,则字段的初始化在编译时发生
不完全 - 它只适用于“最终的静态字段并由编译时常量表达式初始化”:
static final String = "abc"; //compile time constant
static final Object = new Object(); //initialised at runtime
在您的情况下,单元将在加载枚举类时初始化,即第一次在代码中引用enumClazz
。
所以它实际上是懒惰的,除非你在代码中使用枚举的其他地方有声明。
以上是关于通过enum方式的Singleton是否是懒惰初始化?的主要内容,如果未能解决你的问题,请参考以下文章
Enum Singleton中静态getInstance方法的目的是啥?
Spring Boot 中的 Hibernate 无法懒惰地初始化角色集合,无法初始化代理 - 没有 Session 异常