私はちょうどこれを見ました奇妙な別の質問でコードの一部。 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
が自身をビルドするように参照しているため、爆発するだろうと思いました。
なぜそうならないのかわかりません。
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
で終わるコンストラクター呼び出しの無限チェーンに。
変数 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));
}
それが無限の初期化を引き起こさない理由を理解するための鍵は、クラスNode
が初期化されるとき、JVMはそれを追跡し、avoidsの再帰参照中の再初期化です元の初期化内のクラス。これは 言語仕様のこのセクション で詳しく説明されています:
Javaプログラミング言語はマルチスレッドであるため、他のスレッドが同じクラスまたはインターフェイスを同時に初期化しようとする可能性があるため、クラスまたはインターフェイスの初期化には注意深い同期が必要です。クラスまたはインターフェースの初期化の一部として、クラスまたはインターフェースの初期化が再帰的に要求される可能性もあります;例えば、クラスAの変数初期化子無関係なクラスBのメソッドを呼び出し、次にクラスAのメソッドを呼び出す可能性があります。Java仮想マシンの実装は、次の手順。
そのため、静的初期化子が静的インスタンスNIL
を作成している間、コンストラクター呼び出しの一部としてのNode.NIL
への参照は、静的初期化子を再実行しません。代わりに、参照NIL
がその時点で持っている値(この場合はnull
)を参照します。