it-swarm.com.de

Warum löst diese Anweisung keinen StackOverflowError aus?

Ich habe gerade dieses seltsame Stück Code in einer anderen Frage gesehen. Ich dachte, es würde dazu führen, dass StackOverflowError geworfen wird, aber das tut es nicht ...

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

Ich dachte, es würde explodieren, wegen der Node.NIL verweist auf sich selbst, um zu erstellen.

Ich kann nicht herausfinden, warum es nicht so ist.

73
Anthony Raymond

NIL ist eine statische Variable. Es wird einmalig initialisiert, wenn die Klasse initialisiert wird. Bei der Initialisierung wird eine einzelne Instanz von Node erstellt. Die Erstellung dieses Node löst keine Erstellung anderer Node Instanzen aus, sodass es keine unendliche Kette von Aufrufen gibt. Die Übergabe von Node.NIL An den Konstruktoraufruf hat den gleichen Effekt wie die Übergabe von null, da Node.NIL Beim Aufruf des Konstruktors noch nicht initialisiert ist. Daher ist public static Node NIL = new Node(Node.NIL, Node.NIL); dasselbe wie public static Node NIL = new Node(null, null);.

Wenn andererseits NIL eine Instanzvariable war (und nicht als Argument an den Konstruktor Node übergeben wurde, da der Compiler Sie daran gehindert hätte, es an den Konstruktor in zu übergeben In diesem Fall wird es jedes Mal initialisiert, wenn eine Instanz von Node erstellt wird, wodurch eine neue Node -Instanz erstellt wird, deren Erstellung eine andere NIL -Instanzvariable initialisiert, die führend ist zu einer unendlichen Kette von Konstruktoraufrufen, die mit StackOverflowError enden würden.

100
Eran

Der Variable NIL wird zuerst der Wert null gegeben und dann einmal von oben nach unten initialisiert. Es ist kein Funktion und nicht rekursiv definiert. Jedes statische Feld, das Sie verwenden, bevor es initialisiert wird, hat den Standardwert und Ihr Code ist derselbe wie

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

Das ist nicht anders als beim Schreiben

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

Wenn Sie eine Funktion oder Methode wie folgt definieren, erhalten Sie eine StackoverflowException

Node NIL(Node a, Node b) {
    return NIL(NIL(a, b), NIL(a, b));
}
27
Peter Lawrey

Der Schlüssel zum Verständnis, warum dies keine unendliche Initialisierung verursacht, liegt darin, dass die JVM beim Initialisieren der Klasse Node den Überblick behält und vermeidet während einer Rekursion eine Neuinitialisierung durchführt Verweis auf die Klasse innerhalb ihrer ursprünglichen Initialisierung. Dies wird ausführlich in dieser Abschnitt der Sprachspezifikation :

Da die Programmiersprache Java Multithreading) verwendet wird, muss die Initialisierung einer Klasse oder Schnittstelle sorgfältig synchronisiert werden, da ein anderer Thread möglicherweise versucht, dieselbe Klasse oder Schnittstelle zur gleichen Zeit zu initialisieren. Es besteht auch die Möglichkeit, dass die Initialisierung einer Klasse oder eines Interfaces als Teil der Initialisierung dieser Klasse oder dieses Interfaces rekursiv angefordert wird , beispielsweise ein Variableninitialisierer in Klasse A Ruft möglicherweise eine Methode einer nicht verwandten Klasse B auf, die wiederum eine Methode der Klasse A aufruft. Die Implementierung der virtuellen Maschine Java=) ist für die Synchronisierung und rekursive Initialisierung mithilfe von verantwortlich folgende Prozedur.

Während der statische Initialisierer die statische Instanz NIL erstellt, wird der Verweis auf Node.NIL führt im Rahmen des Konstruktoraufrufs den statischen Initialisierer nicht erneut aus. Stattdessen wird nur auf den Wert verwiesen, den die Referenz NIL zu diesem Zeitpunkt hat, in diesem Fall also null.

20
manouti