为什么这个语句不会抛出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
。