为什么这个语句不会抛出StackOverflowError?

在另一个问题中,我刚刚看到这个奇怪的代码。 我认为这会导致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; } } 

我以为它会爆炸,因为Node.NIL引用自己构build。

我无法弄清楚为什么没有。

NIL是一个静态variables。 它初始化一次,当这个类被初始化的时候。 初始化时,创build一个Node实例。 该Node的创build不会触发任何其他Node实例的创build,因此不存在无限的调用链。 将Node.NIL传递给构造函数的调用与传递null效果相同,因为Node.NIL在调用构造函数时尚未初始化。 因此public static Node NIL = new Node(Node.NIL, Node.NIL);public static Node NIL = new Node(null, null);

另一方面,如果NIL是一个实例variables(并且没有作为parameter passing给Node构造函数,因为编译器在这种情况下不能将它传递给构造函数),所以每次都会初始化它Node一个实例被创build,它将创build一个新的Node实例,其创build将初始化另一个NIL实例variables,从而导致以StackOverflowError结尾的无限的构造函数调用链。

首先给variables NIL赋值null ,然后从上到下初始化。 这不是一个函数 ,也不是recursion定义的。 您在初始化之前使用的任何静态字段都具有默认值,您的代码与之相同

 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 } } 

这与写作没有什么不同

 NIL = null; // set implicitly NIL = new Node(NIL, NIL); 

如果你定义了一个像这样的函数方法 ,你会得到一个StackoverflowException

 Node NIL(Node a, Node b) { return NIL(NIL(a, b), NIL(a, b)); } 

理解为什么它不会导致无限初始化的关键是,当初始化类Node时,JVM会跟踪它,并避免在初始化初始化期间对类进行recursion引用期间的重新初始化。 这在语言规范的这一部分详细说明 :

因为Java编程语言是multithreading的,所以类或接口的初始化需要仔细的同步,因为其他线程可能试图同时初始化相同的类或接口。 类或接口的初始化也可能被recursion地请求作为该类或接口的初始化的一部分 ; 例如,A类中的一个variables初始值设定项可能会调用一个不相关的类B的方法,而这个方法又可能调用A类的方法。Java虚拟机的实现负责处理同步和recursion初始化遵循程序。

因此,虽然静态初始化程序正在创build静态实例NIL ,但作为构造函数调用一部分的对Node.NIL的引用不会再次重新执行静态初始化程序。 相反,它只是引用当时引用NIL具有的null在这种情况下为null