3 回答
TA贡献1818条经验 获得超11个赞
因为静态元素是按照在源代码中给出的顺序进行初始化的。
看一下这个:
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或默认值。
在第二个静态变量的初始化期间,第一个静态变量是正确的,但其余的仍为null或默认值。
明白了吗?
编辑2
正如Varman指出的那样,在初始化时对其自身的引用将为null。如果考虑一下,这是有道理的
TA贡献1891条经验 获得超3个赞
让我们尝试另一种方式来解释这一点...
这是您首次引用该类时JVM经历的顺序MyClass
。
将字节码加载到内存中。
清除了用于静态存储的内存(二进制零)。
初始化类:
按照显示顺序执行每个静态初始化程序,其中包括静态变量和
static { ... }
块。然后,JVM将您的
myClass
静态变量初始化为的新实例MyClass
。发生这种情况时,JVM会注意到
MyClass
已加载(字节码)并且正在初始化,因此它会跳过初始化。在堆上为对象分配内存。
执行构造函数。
打印出
obj
仍然是静态的值null
(因为它不是堆和构造函数初始化变量的一部分)。构造函数完成后,执行下一个静态初始值设定项
obj
,将其设置为的新实例Object
。类初始化完成。从这一点来看,所有构造函数调用都将按照您的假定/期望的方式运行-这
obj
将不仅仅是null
对Object
实例的引用。
请记住,Java指定给final
变量分配一次值。并不是确保在代码引用它时为它分配一个值,除非您确保在赋值后代码引用了它。
这不是错误。这是在类自身初始化期间处理类使用的已定义方法。如果不是这样,那么JVM将陷入无限循环。请参阅步骤#3.3(如果JVM不跳过正在初始化过程中的类的初始化,它将继续对其进行初始化-无限循环)。
还要注意,这一切都发生在首先引用该类的同一线程上。其次,JVM保证初始化将在允许任何其他线程使用此类之前完成。
TA贡献1874条经验 获得超12个赞
这是因为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
}
}
一般来说,最好避免一起使用这种构造。如果您尝试创建一个单例,那么该代码片段应如下所示:
class MyClass {
private static final MyClass myClass = new MyClass();
private Object obj = new Object();
private MyClass() {
System.out.println(obj); // will print null once
}
}
添加回答
举报