Ant*_*ond 73 java stack-overflow
我刚刚在另一个问题中看到了这段奇怪的代码.我认为这会导致StackOverflowError
被抛出,但它不会......
public class Node {
private Object one;
private Object two;
public static Node NIL = new Node(Node.NIL, Node.NIL);
public Node(Object one, Object two) {
this.one = one;
this.two = two;
}
}
Run Code Online (Sandbox Code Playgroud)
我认为它会爆炸,因为Node.NIL
引用它自己构建.
我无法弄明白为什么它没有.
Era*_*ran 100
NIL
是一个静态变量.初始化类时,它会初始化一次.初始化时,Node
会创建一个实例.创建Node
它不会触发任何其他Node
实例的创建,因此没有无限的调用链.传递Node.NIL
给构造函数调用与传递具有相同的效果null
,因为Node.NIL
在调用构造函数时尚未初始化.因此public static Node NIL = new Node(Node.NIL, Node.NIL);
是一样的public static Node NIL = new Node(null, null);
.
另一方面,如果NIL
是一个实例变量(并且没有作为参数传递给Node
构造函数,因为在这种情况下编译器会阻止您将它传递给构造函数),每次实例都会初始化它of Node
created,它将创建一个新Node
实例,其创建将初始化另一个NIL
实例变量,从而导致无限的构造函数调用链结束StackOverflowError
.
Pet*_*rey 27
所述可变 NIL首先给出的值null
,然后初始化一次从上到下.它不是一个函数,也不是递归定义的.在初始化之前使用的任何静态字段都具有默认值,并且您的代码与之相同
public static Node {
public static Node NIL;
static {
NIL = new Node(null /*Node.NIL*/, null /*Node.NIL*/);
}
public Node(Object one, Object two) {
// Assign values to fields
}
}
Run Code Online (Sandbox Code Playgroud)
这与写作没什么不同
NIL = null; // set implicitly
NIL = new Node(NIL, NIL);
Run Code Online (Sandbox Code Playgroud)
如果你定义了这样的函数或方法,你会得到一个StackoverflowException
Node NIL(Node a, Node b) {
return NIL(NIL(a, b), NIL(a, b));
}
Run Code Online (Sandbox Code Playgroud)
man*_*uti 20
理解为什么它不会导致无限初始化的关键在于,当Node
初始化类时,JVM会跟踪它并避免在其原始初始化内对类的递归引用期间重新初始化.这在语言规范的这一节中有详细说明:
由于Java编程语言是多线程的,因此初始化类或接口需要仔细同步,因为某些其他线程可能正在尝试同时初始化相同的类或接口.作为该类或接口的初始化的一部分,还可以递归地请求类或接口的初始化 ; 例如,类A中的变量初始化程序可能会调用不相关的类B的方法,而该方法又可能调用类A的方法.通过使用该方法,Java虚拟机的实现负责处理同步和递归初始化.以下程序.
因此,当静态初始化程序创建静态实例时NIL
,Node.NIL
作为构造函数调用的一部分的引用不会再次重新执行静态初始化程序.相反,它只引用当时引用所NIL
具有的任何值,null
在这种情况下.