为啥静态字段没有及时初始化?
Posted
技术标签:
【中文标题】为啥静态字段没有及时初始化?【英文标题】:Why static fields are not initialized in time?为什么静态字段没有及时初始化? 【发布时间】:2011-02-02 14:42:24 【问题描述】:以下代码打印一次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 个 Object 字段,现在我看到第一个是 MyClass 字段.. :/
【问题讨论】:
【参考方案1】:因为静态是按照它们在源代码中给出的顺序初始化的。
看看这个:
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
编辑
好的,让我们把它画得更清楚一点。
-
静态数据按照源代码中声明的顺序一一初始化。
由于第一个静态字段在其余静态字段之前初始化,因此在初始化期间,其余静态字段为空值或默认值。
在第二个静态初始化期间,第一个静态是正确的,但其余的仍然是 null 或默认值。
清楚吗?
编辑 2
正如 Varman 指出的那样,在初始化时,对自身的引用将为空。如果您考虑一下,这是有道理的。
【讨论】:
..因为myClass
本身是静态的。
好的,按顺序初始化静态。而且静态在构造函数之前,为什么构造函数运行时没有初始化呢?对我来说真的像一个错误..
@Tom,不,你弄错了。静态不在构造函数之前。当分别调用构造函数时,静态是初始化的。在我的示例中,当第一个静态是使用 MyClass 初始化时,将调用构造函数。当构造函数运行时 myClass 是 init (因为它自己运行),但 myClass2 不是。当第二个 MyClass 是 init 时,它再次调用构造函数,这次 myClass 已经 init 并且 myClass2 那时正在 init
所以在构造函数运行时,它的静态变量在另一个线程中初始化?您是否有指向相同文本的链接,详细说明了这一点?
@Pyrolistical :当我执行你的程序时,我得到了不同的结果。它打印了null null myClassObjectref null
【参考方案2】:
让我们尝试一种不同的方式来解释这一点......
这是当您第一次引用类 MyClass
时 JVM 执行的顺序。
-
将字节码加载到内存中。
静态存储的内存被清除(二进制零)。
初始化类:
-
按出现的顺序执行每个静态初始化程序,这包括静态变量和
static ...
块。
JVM 然后将您的myClass
静态变量初始化为MyClass
的新实例。
发生这种情况时,JVM 注意到 MyClass
已经加载(字节码)并且正在初始化,因此它会跳过初始化。
在堆上为对象分配内存。
执行构造函数。
打印出obj
的值,它仍然是null
(因为它不是堆和构造函数初始化变量的一部分)。
构造函数完成后,执行下一个静态初始化程序,将obj
设置为Object
的新实例。
obj
将不是 null
,而是对 Object
实例的引用。
请记住,Java 指定 final
变量被赋值一次。不是保证在代码引用它的时候就一定给它赋值,除非你保证在赋值之后代码引用它。
这不是错误。这是在类自身初始化期间处理类使用的已定义方式。如果不是这样,那么 JVM 将进入无限循环。请参见步骤 #3.3(如果 JVM 不跳过初始化过程中的类的初始化,它只会继续初始化它 - 无限循环)。
请注意,这一切都发生在第一次引用该类的同一个线程上。其次,JVM 保证初始化将在任何其他线程被允许使用该类之前完成。
【讨论】:
很好的答案凯文,这里最好的一个 你的意思是它跳过了“静态”初始化,我想。【参考方案3】:这是因为 Java 按照声明的顺序执行静态部分。在你的情况下,序列是
-
新的我的班级
新对象
#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
一般来说,最好避免这样的构造。如果您尝试创建一个单例,那么该代码片段应该是这样的:
class MyClass
private static final MyClass myClass = new MyClass();
private Object obj = new Object();
private MyClass()
System.out.println(obj); // will print null once
【讨论】:
更正了new MyClass()
来理解你调用构造函数的静态
认为我们的通信失败了。我实际上知道 new MyClass() 是对构造函数的调用,这并不能解释为什么构造函数运行时静态字段为空。就像实例字段在构造函数之前初始化一样,静态字段应该是……但为什么不呢?
静态成员 在实例构造函数运行之前构造。只是在你的代码中,你的静态初始化器也调用你的构造器。这是一个先有鸡还是先有蛋的问题。【参考方案4】:
这是因为静态字段按照它们定义的顺序初始化。
【讨论】:
这没有回答为什么它在构造函数时为空。【参考方案5】:@Pyrolistical
由于第一个静态字段 myclass 的初始没有完全构造...我得到的结果是
空 空值 testInitialize.MyObject@70f9f9d8 空
【讨论】:
以上是关于为啥静态字段没有及时初始化?的主要内容,如果未能解决你的问题,请参考以下文章