我看到了这样的答案,试图通过评论来澄清,但对这里的例子不满意。
也许是时候回答这个具体问题了...
为什么枚举单例实现称为惰性?
public enum EnumLazySingleton {
INSTANCE;
EnumLazySingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
它与急切实施有何不同?
public class BasicEagerSingleton {
private static final BasicEagerSingleton instance = new BasicEagerSingleton();
public static BasicEagerSingleton getInstance() {
return instance;
}
private BasicEagerSingleton() {
System.out.println("constructing: " + this);
}
public static void touchClass() {}
}
两者都将在不访问INSTANCE/getInstance()
的情况下初始化实例 - 例如调用touchClass()
.
public class TestSingleton {
public static void main(String... args) {
System.out.println("sleeping for 5 sec...");
System.out.println("touching " + BasicEagerSingleton.class.getSimpleName());
BasicEagerSingleton.touchClass();
System.out.println("touching " + EnumLazySingleton.class.getSimpleName());
EnumLazySingleton.touchClass();
}
}
输出:
sleeping for 5 sec...
touching BasicEagerSingleton
constructing: BasicEagerSingleton@7bfcd12c
touching EnumLazySingleton
constructing: INSTANCE
现在,我们可以说两者都是懒惰的。那么什么是急切呢?
很明显(例如)"双重检查锁定"方式实际上是懒惰的(而且混乱且缓慢)。但是如果枚举是懒惰的,那么由于不可避免的类加载,任何单例都是懒惰的——事实上,一切都是懒惰的。在什么时候,这种区别将不再有意义?
前两个链接的答案(由Peter Lawrey和Joachim Sauer)都同意枚举不是懒惰初始化的。第三个链接中的答案对于延迟初始化的含义是完全错误的。
使用枚举作为单例的建议源自Josh Bloch的Effective Java。值得注意的是,关于枚举单例的章节没有提到懒惰。后面有一章专门讨论延迟初始化,同样没有提到枚举。本章包含两个亮点。
- 如果需要使用延迟初始化来提高静态字段的性能,请使用延迟初始化持有者类习惯用法。
- 如果您需要使用延迟初始化来提高实例字段的性能,请使用仔细检查习惯用法。
毫无疑问,如果枚举以任何方式延迟初始化,枚举将是此列表中的另一个成语。事实上,它们不是,尽管对延迟初始化含义的混淆会导致一些不正确的答案,如 OP 所示。
我可以打赌以下内容:
您正在尝试识别 2 个"进程"或......"things"
(让我们让它变得容易理解 - 因为如果我开始说"代码块",听起来更难)......
- 运行,并且您想知道当类装入器装入类时将执行什么
"things"
。 - 在另一点调用类上的方法将导致另一个
"thing"
运行/执行,并且您想知道哪个(哪个"processes"
)将启动。
以下事实相关:
- 静态初始值设定项在类装入器装入类时运行。 类装入器不会装入该类,直到 运行会遇到加载它的需要(因为方法或字段具有 被调用),例如:
touchClass()
- 如果类或枚举类型的单例实例具有在类的
static
部分中初始化的field
,则一旦您"触摸"该类 -因为类加载器在装入时运行类或枚举的所有static initializations
。- 延迟加载,很可能,(这是我对你所问内容的"解释")会在方法调用询问类时发生 创建一个单一实例 -这可能会发生很多
class
或enum
"加载"后的时间.
如下所示的类:
public class LazySingleton
{
// At time of class-loading, this singleton is set to 'null'
private static singleton = null;
// This is a method that will not be invoked until it is called by
// some other code-block (some other "thing")... When "touchClass()"
// is called, the singleton instance is not created.
public static LazySingleton retrieveSingleton()
{
if (singleton == null) singleton = new LazySingleton();
return singleton;
}
// DOES NOTHING... The Singleton is *not* loaded, even though the
// Class Loader has already loaded this Java ".class" file
// into memory.
public static void touchClass() { }
private LazySingleton()
{ System.out.println("constructing: LazySingleton"); }
}
另一方面:
public enum EagerEnum
{
// The class loader will run this constructor as soon as this 'enum'
// is loaded from a '.class' file (in JAR or on disk) into memory
MyEnumConstant();
private EagerEnum()
{ System.out.println("Eager Enum Constructed"); }
// This will cause the Class Loader to Load this enum from the
// Java ".class" File immediately, and the "MyEnumConstant" will
// also have to be loaded - meaning the constructor will be called.
public static void touchEnum() { }
}
所以下面的代码将产生输出
LazySingleton.touchClass(); // Prints nothing
EagerEnum.touchClass(); // Prints "Eager Enum Constructed"
LazySingleton.getSingletonInstance(); // Prints "constructing: LazySingleton