在Singleton程序中使用和流动静态语句

我知道有很多关于Singleton模式的问题。 但是在这里我想知道关于输出的内容,它也可能涵盖了Java中“静态”的工作方式。

public class Singleton { private static Singleton currentSingleton = new Singleton(); public static Singleton getSingleton() { return currentSingleton; } private Singleton() { System.out.println("Singleton private constructor..."); } public static void main(String[] args) { System.out.println("Main method..."); } 

}

这是运行代码的输出…

Singleton私有构造函数…
主要方法……

当我调试这段代码时,控件首先进入System.out.println("Singleton private constructor...")并打印。 (私有静态变量currentSingleton此时仍为null)
然后就行了
private static Singleton currentSingleton = new Singleton(); 然后初始化私有变量。 最后,它进入main()方法并打印。

我的问题是:

  1. 为什么它首先打印私有构造函数中的“Singleton私有构造函数…”。 我认为控制首先应该转到main()方法,因为它是入口点。 另外,我没有在任何地方创建任何实例(除了在变量初始化中)。
  2. 后来转到静态变量实例化行(此时currentSingleton = null)
    private static Singleton currentSingleton = new Singleton(); 虽然currentSingleton在这里得到一个值,为什么不再调用构造函数?

主要是我想知道这个程序的控制流程。

在正确初始化之前,您无法调用类中的main方法(即已评估静态字段和静态块)。 初始化时,通过调用私有构造函数创建单例的实例。 稍后调用main方法。

有问题的类有一个静态字段,您可以为其赋值。 由于该字段是静态的,因此必须先初始化该类,然后才能在任何上下文中使用该类,也就是说,它必须接收一个值。 在这种情况下,它的值恰好是同一个类的实例。 这是在类初始化期间触发私有costructor的原因。

如果您想深入了解该过程并更好地理解它,请参阅Java Laguage规范 。 更具体地说,在第12.4节“类和接口的初始化”中,您将找到更多详细信息。

每当控件第一次进入任何类时,所有静态初始化都会首先发生。 这就是为什么你的静态对象在调用它的构造函数之前被实例化的原因。

类首先由JVM的类加载器加载和初始化。 和JVM在初始化时,扫描类(Singleton)并在执行此操作时初始化第一行中的静态变量。 该变量调用构造函数并在其中打印行。 初始化类后,它将调用main方法,从而在其中打印语句。

在控件进入main()方法之前,需要初始化类。 由于您使用声明初始化currentSingleton内联,因此在类加载期间,此初始化发生在main()之前。

你应该把它宣布为final

 private static final Singleton currentSingleton = new Singleton(); 
  1. 初始化最终类变量和其值为编译时常量的接口字段

从JLS#8.3.2回答你的两个问题。 字段的初始化

如果声明符用于类变量(即静态字段),则在初始化类时(第12.4.2节),将对变量初始值设定项进行求值并执行一次赋值。

new Singleton()语句是第一个被执行的语句,因为必须初始化currentSingleton静态字段; 这意味着分配正在创建的Singleton对象的内存,并在将结果对象分配给字段变量之前执行其构造函数。 这就是System.out.println("Singleton private constructor..."); 在分配之前执行的行。 此外,一旦引用了类,就会发生静态字段初始化,并且调用Singleton类的main函数意味着引用它,这就是在main方法之前执行初始化的原因。