您好,我正在尝试了解 JMM 以及为什么某些操作会以它们的方式工作。
我想了解为什么以下使用静态变量和静态初始化程序的方法与不同按需初始化(IODH)习惯用法。
选项1:仅初始化静态变量
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
}
选项 2:静态初始化器
public class Singleton {
private static Singleton instance = null;
static {
instance = new Singleton();
}
private Singleton() {
}
public static Singleton getInstance() { return instance;}
}
为什么我认为这些应该与 IODH 惯用语相同:
静态字段的显式初始化程序作为 初始化
我哪里出错了?
它们(选项 1 和 2)与 IODH 不同。 IODH 的目的是指定当调用
getInstance()
方法时将初始化静态字段,并且不会以任何方式触及该类。因此它使静态引用初始化更加明确。作为参考,IODH 是这样的:
public class Singleton {
private Singleton() {}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
在急切初始化的情况下,每当运行时在代码中引用类时,都会加载类定义(来自 .class 文件)并初始化其所有静态成员(包括静态块)。 例如,如果您的单例类中有这样的方法,并且在客户端代码中引用它,(
Singleton.doSomething
),您将看到该类被实例化,尽管您没有显式调用 getInstance()
方法,因为静态成员正在初始化。
public class Singleton {
private Singleton() {
System.out.println("Singleton initialized");
}
private static Singleton instance = new Singleton();
private Singleton() {}
public static Singleton getInstance() {
return instance;
}
public static void doSomething() {
System.out.println("Singleton doing something");
}
}
但是,当您对 IODH 单例执行相同的操作时,只会将
"LazyRegistryIODH doSomething"
消息打印到控制台,并且 LazyRegistryIODH
的构造函数不会被调用,因为没有要初始化的静态字段或块。
public class LazyRegistryIODH {
private LazyRegistryIODH() {
System.out.println("LazyRegistryIODH initialized");
}
private static class Holder {
static final LazyRegistryIODH INSTANCE = new LazyRegistryIODH();
}
protected static void doSomething() {
System.out.println("LazyRegistryIODH doSomething");
}
public static LazyRegistryIODH getInstance() {
return Holder.INSTANCE;
}
}