Why doesn't this statement throw a StackOverflowError?

I just saw this weird piece of code in another question. I thought this would throw a StackOverflowError , but this is not ...

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

I thought it would explode due to Node.NIL referring to itself for assembly.

I can’t understand why this is not so.

+74
java stack-overflow
Jan 30 '17 at 9:37 on
source share
3 answers

NIL is a static variable. It is initialized once when the class is initialized. When it is initialized, one instance of Node . Creating this Node does not cause any other Node instances to be created, so there is no endless chain of calls. Passing Node.NIL to the constructor call has the same effect as passing null , since Node.NIL is not yet initialized when the constructor is called. Therefore, public static Node NIL = new Node(Node.NIL, Node.NIL); matches public static Node NIL = new Node(null, null); .

If, on the other hand, NIL was an instance variable (and was not passed as an argument to the Node constructor, since the compiler would prevent you from passing it to the constructor in this case), it will be initialized every time a Node instance is created that would create a new an Node instance, the creation of which would initialize another NIL instance variable, which would lead to an infinite chain of constructor calls that would end in a StackOverflowError .

+100
Jan 30 '17 at 9:39 on
source share

In variable NIL is first set to null , and then initialized once from top to bottom. This is not a function and is not defined recursively. Any static field that you use before its initialization has a default value, and your code is the same as

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

It is no different from writing

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

If you define a function or method like this, you will get a StackoverflowException

 Node NIL(Node a, Node b) { return NIL(NIL(a, b), NIL(a, b)); } 
+27
Jan 30 '17 at 9:44 on
source share

The key to understanding why it does not cause infinite initialization is that when the Node class is initialized, the JVM monitors it and avoids re-initialization during the recursive reference to the class in its initial initialization. This is described in detail in this section of the language specification :

Because the Java programming language is multi-threaded, initializing a class or interface requires careful synchronization, as some other threads may try to initialize the same class or interface at the same time. There is also the possibility that the initialization of a class or interface may be requested recursively as part of the initialization of this class or interface ; for example, a variable initializer in class A can call a method of an unrelated class B, which, in turn, can call a method of class A. The implementation of the Java virtual machine is responsible for synchronization and recursive initialization using the following procedure.

Therefore, when the static initializer creates a static instance of NIL , the reference to Node.NIL as part of the constructor invocation does not repeat the restart of the static initializer. Instead, it simply refers to any value that has a reference NIL value, which in this case is null .

+20
Jan 30 '17 at 9:49 on
source share



All Articles