为什么静态字段没有及时初始化?

下面的代码打印null一次。

 class MyClass { private static MyClass myClass = new MyClass(); private static final Object obj = new Object(); public MyClass() { System.out.println(obj); } public static void main(String[] args) {} } 

为什么在构造函数运行之前静态对象没有被初始化?

更新

我刚刚复制这个示例程序没有注意,我想我们正在谈论2个对象字段,现在我看到,第一个是MyClass字段..:/

因为静态按照它们在源代码中给出的顺序进行初始化。

看一下这个:

 class MyClass { private static MyClass myClass = new MyClass(); private static MyClass myClass2 = new MyClass(); public MyClass() { System.out.println(myClass); System.out.println(myClass2); } } 

这将打印:

 null null myClassObject null 

编辑

好吧,让我们画出更清晰一点。

  1. 静态按照源代码中声明的顺序逐个进行初始化。
  2. 由于第一个静态在初始化之前被初始化,所以在其初始化过程中其余的静态字段为空或缺省值。
  3. 在启动第二个静态过程中,第一个静态是正确的,但其余静态仍然是空的或默认的。

这清楚吗?

编辑2

正如Varman所指出的那样,在初始化的时候,对自身的引用将是空的。 如果你考虑一下,这是有道理的。

让我们尝试一种不同的方式来解释这个…

这是JVM在首次引用类MyClass时所经历的顺序。

  1. 将字节码加载到内存中。
  2. 清除静态存储器的内存(二进制零)。
  3. 初始化类:
    1. 按照出现的顺序执行每个静态初始化器,这包括静态variables和static { ... }块。
    2. 然后JVM将您的myClass静态variables初始化为一个MyClass的新实例。
    3. 当发生这种情况时,JVM注意到MyClass已经被加载(字节码), 并且正在被初始化 ,所以它跳过了初始化。
    4. 在堆上为内存分配内存。
    5. 执行构造函数。
    6. 打印出仍然为nullobj null (因为它不是堆和构造函数初始化variables的一部分)。
    7. 当构造函数完成时,执行下一个将obj设置为Object的新实例的静态初始化程序。
  4. 类初始化完成。 从这一点来看,所有的构造函数调用都会按照您的设想/期望行事 – 即obj不会是null而是对Object实例的引用。

请记住,Java指定finalvariables被赋值一次。 这并不是说当代码引用它的时候,它是保证被赋值的,除非你确保代码在赋值之后引用它。

这不是一个错误。 这是在自己初始化期间处理类的使用的定义方式。 如果不是这样,那么JVM就会陷入无限循环。 请参阅步骤#3.3(如果JVM不跳过正在初始化的类的初始化,它将继续初始化它 – 无限循环)。

请注意,这一切都发生在首先引用该类的同一个线程上。 其次,JVM保证在任何其他线程被允许使用这个类之前完成初始化。

这是因为Java为了声明而执行静态部分。 在你的情况下,序列是

  1. 新的MyClass
  2. 新对象

当执行#1时,obj还没有被初始化,所以它打印出null。 尝试以下,你会看到不同之处:

 class MyClass { private static final Object obj = new Object(); private static MyClass myClass = new MyClass(); public MyClass() { System.out.println(obj); // will print null once } } 

一般来说,最好避免这样的结构。 如果你正在尝试创build一个单例,那么这个代码片段应该是这样的:

 class MyClass { private static final MyClass myClass = new MyClass(); private Object obj = new Object(); private MyClass() { System.out.println(obj); // will print null once } } 

那是因为静态字段以它们定义的相同顺序初始化。

@Pyrolistical

因为第一个静态字段myclass的初始化没有完全构造…我得到的结果是

null null testInitialize.MyObject@70f9f9d8 null