web-dev-qa-db-ja.com

このステートメントが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が自身をビルドするように参照しているため、爆発するだろうと思いました。

なぜそうならないのかわかりません。

73
Anthony Raymond

NILは静的変数です。クラスが初期化されると、1回初期化されます。初期化されると、単一の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コンストラクターへの引数として渡されなかった場合、コンパイラーがコンストラクターに渡すことを妨げていたため)その場合)、Nodeのインスタンスが作成されるたびに初期化され、新しいNodeインスタンスが作成され、その作成により別のNILインスタンス変数が初期化され、 StackOverflowErrorで終わるコンストラクター呼び出しの無限チェーンに。

100
Eran

変数 NILには最初に値nullが与えられ、次に上から下に一度初期化されます。 functionではなく、再帰的に定義されていません。初期化する前に使用する静的フィールドにはデフォルト値があり、コードは次と同じです。

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

このようにfunctionまたはmethodを定義した場合、StackoverflowExceptionが発生します

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

それが無限の初期化を引き起こさない理由を理解するための鍵は、クラスNodeが初期化されるとき、JVMはそれを追跡し、avoidsの再帰参照中の再初期化です元の初期化内のクラス。これは 言語仕様のこのセクション で詳しく説明されています:

Javaプログラミング言語はマルチスレッドであるため、他のスレッドが同じクラスまたはインターフェイスを同時に初期化しようとする可能性があるため、クラスまたはインターフェイスの初期化には注意深い同期が必要です。クラスまたはインターフェースの初期化の一部として、クラスまたはインターフェースの初期化が再帰的に要求される可能性もあります;例えば、クラスAの変数初期化子無関係なクラスBのメソッドを呼び出し、次にクラスAのメソッドを呼び出す可能性があります。Java仮想マシンの実装は、次の手順。

そのため、静的初期化子が静的インスタンスNILを作成している間、コンストラクター呼び出しの一部としてのNode.NILへの参照は、静的初期化子を再実行しません。代わりに、参照NILがその時点で持っている値(この場合はnull)を参照します。

20
manouti