为什么静态字段没有及时初始化?
下面的代码打印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
编辑
好吧,让我们画出更清晰一点。
- 静态按照源代码中声明的顺序逐个进行初始化。
- 由于第一个静态在初始化之前被初始化,所以在其初始化过程中其余的静态字段为空或缺省值。
- 在启动第二个静态过程中,第一个静态是正确的,但其余静态仍然是空的或默认的。
这清楚吗?
编辑2
正如Varman所指出的那样,在初始化的时候,对自身的引用将是空的。 如果你考虑一下,这是有道理的。
让我们尝试一种不同的方式来解释这个…
这是JVM在首次引用类MyClass
时所经历的顺序。
- 将字节码加载到内存中。
- 清除静态存储器的内存(二进制零)。
- 初始化类:
- 按照出现的顺序执行每个静态初始化器,这包括静态variables和
static { ... }
块。 - 然后JVM将您的
myClass
静态variables初始化为一个MyClass
的新实例。 - 当发生这种情况时,JVM注意到
MyClass
已经被加载(字节码), 并且正在被初始化 ,所以它跳过了初始化。 - 在堆上为内存分配内存。
- 执行构造函数。
- 打印出仍然为
null
的obj
null
(因为它不是堆和构造函数初始化variables的一部分)。 - 当构造函数完成时,执行下一个将
obj
设置为Object
的新实例的静态初始化程序。
- 按照出现的顺序执行每个静态初始化器,这包括静态variables和
- 类初始化完成。 从这一点来看,所有的构造函数调用都会按照您的设想/期望行事 – 即
obj
不会是null
而是对Object
实例的引用。
请记住,Java指定final
variables被赋值一次。 这并不是说当代码引用它的时候,它是保证被赋值的,除非你确保代码在赋值之后引用它。
这不是一个错误。 这是在自己初始化期间处理类的使用的定义方式。 如果不是这样,那么JVM就会陷入无限循环。 请参阅步骤#3.3(如果JVM不跳过正在初始化的类的初始化,它将继续初始化它 – 无限循环)。
请注意,这一切都发生在首先引用该类的同一个线程上。 其次,JVM保证在任何其他线程被允许使用这个类之前完成初始化。
这是因为Java为了声明而执行静态部分。 在你的情况下,序列是
- 新的MyClass
- 新对象
当执行#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