静的変数の初期化シーケンス
1. 簡単なルール
まず、最も一般的な JAVA コードを見てみましょう:
public class Test { public static Test1 t = new Test1(); public static int a = 0; public static int b; public static void main(String[] arg) { System.out.println(Test.a); System.out.println(Test.b); } } class Test1 { public Test1() { Test.a++; Test.b++; } }
ここで、まずコンソール出力が何であるかを推測してみましょう?
OK、おそらく以下の結果はすでに推測しているでしょう。まだ Java に精通しています。
0 1
上記の結果が出力される理由がわからない場合は、教えてください。
Java 静的変数の初期化は、次の規則に従います。
静的変数は、宣言の順序で宣言され、その型のデフォルト値に設定されますが、初期化された値は割り当てられません。
宣言が完了したら、宣言の順に初期化値を設定します。初期化値がない場合はスキップします。
これを読むとわかると思いますが、Test.a の値が 3 回変更されたことがわかります。
宣言時に 0 に設定>>Test1::Test1 で 1 に設定>>Test.a は 0 に初期化されます
2 複雑なルール
これを理解して、以下のコードを見てください。
public class A { public static int b = B.a; public static A plus =new A("A"); public static final int finalInt = (int)(Math.random()*100); public static B p = new B("A"); public static final String finalStr = "finalStr"; public static final Integer finalInteger = new Integer(10); public static int a = 1; public static B c = null; public A(String from) { System.out.println("----------- begin A::A ----------------"); System.out.println("A::A, from="+from); System.out.println("A::A, A.b="+A.b); System.out.println("A::A, A.finalInt="+A.finalInt); System.out.println("A::A, B.a="+B.a); System.out.println("A::A, B.plus="+B.plus); System.out.println("----------- end A::A ----------------"); } public static void main(String[] arg) { System.out.println("main, A.b="+A.b); System.out.println("main, B.t="+B.t); System.out.println("main, C.a="+C.a); } } class B { public static int t = A.a; public static A plus = new A("B"); public static int a = 1; public B(String from) { System.out.println("----------- begin B::B ----------------"); System.out.println("B::B, from="+from); System.out.println("B::B, B.a="+B.a); System.out.println("B::B, A.a="+A.a); System.out.println("B::B, A.p="+A.p); System.out.println("B::B, A.plus="+A.plus); System.out.println("B::B, A.finalInt="+A.finalInt); System.out.println("B::B, A.finalInteger="+A.finalInteger); System.out.println("B::B, A.finalStr="+A.finalStr); System.out.println("----------- end B::B ----------------"); } } class C { public static final A a = new A("C"); }
まだ出力結果を推測できますか?私はテスト中に書いていたので推測できませんでした(笑)
コンソールの出力結果は次のとおりです:
----------- begin A::A ---------------- A::A, from=B A::A, A.b=0 A::A, A.finalInt=0 A::A, B.a=0 A::A, B.plus=null ----------- end A::A ---------------- ----------- begin A::A ---------------- A::A, from=A A::A, A.b=1 A::A, A.finalInt=0 A::A, B.a=1 A::A, B.plus=A@a90653 ----------- end A::A ---------------- ----------- begin B::B ---------------- B::B, from=A B::B, B.a=1 B::B, A.a=0 B::B, A.p=null B::B, A.plus=A@1fb8ee3 B::B, A.finalInt=61 B::B, A.finalInteger=null B::B, A.finalStr=finalStr ----------- end B::B ---------------- main, A.b=1 main, B.t=0 ----------- begin A::A ---------------- A::A, from=C A::A, A.b=1 A::A, A.finalInt=61 A::A, B.a=1 A::A, B.plus=A@a90653 ----------- end A::A ---------------- main, C.a=A@61de33
この結果は推測できませんでした
プログラムの実行結果を一文ずつ説明するには、やはり多くのスペースを必要とします。ここでは、Java の静的変数の初期化に従う規則を直接書きます。
最初の段落のルールはまだ有効ですが、健全ではありません。
クラスを積極的にリクエストした場合にのみ、このクラスには静的変数、関数、その他の静的なもののみが初期化されます。
継承関係では、最初に親クラスが初期化され、次にサブクラスが初期化されます。
静的変数は宣言された通りに宣言されます 最初に宣言されて型のデフォルト値が設定されますが、初期化された値は代入されません 宣言が完了すると、宣言された順序で初期化された値が設定されます。初期化された値がない場合はスキップします A.b = B.a を初期化する場合、A.b の初期化を一時停止し、現在のクラスを B に設定し、ステップ 3 にジャンプして実行します。 B.plus = new A を初期化する場合、 B.plus の初期化を一時停止し、A をインスタンス化して B .plus. に代入します A のコンストラクターで B.a の値を取得する必要がある場合、B.a はまだ初期化されており、初期化一時停止状態にあります。 B.a の初期化を待たずに、直接取得されます。最終的な静的定数は、実際には通常の静的変数の初期化に従いますが、コンパイル中に、コンパイラーは、使用されている不変の定数値を置き換えます。 静的データの初期化と静的修飾されたフィールド。いわゆるクラス フィールドは、このフィールドの所有者がオブジェクトではなくクラスであることを意味します。作成されるオブジェクトの数に関係なく、静的データのコピーは 1 つだけです。
class Bowl { Bowl(int marker) { print("Bowl(" + marker + ")"); } void f1(int marker) { print("f1(" + marker + ")"); } } class Table { static Bowl bowl1 = new Bowl(1); Table() { print("Table()"); bowl2.f1(1); } void f2(int marker) { print("f2(" + marker + ")"); } static Bowl bowl2 = new Bowl(2); } class Cupboard { Bowl bowl3 = new Bowl(3); static Bowl bowl4 = new Bowl(4); Cupboard() { print("Cupboard()"); bowl4.f1(2); } void f3(int marker) { print("f3(" + marker + ")"); } static Bowl bowl5 = new Bowl(5); } public class StaticInitialization { public static void main(String[] args) { print("Creating new Cupboard() in main"); new Cupboard(); print("Creating new Cupboard() in main"); new Cupboard(); table.f2(1); cupboard.f3(1); } static Table table = new Table(); static Cupboard cupboard = new Cupboard(); }
Bowl(1) Bowl(2) Table() f1(1) Bowl(4) Bowl(5) Bowl(3) Cupboard() f1(2) Creating new Cupboard() in main Bowl(3) Cupboard() f1(2) Creating new Cupboard() in main Bowl(3) Cupboard() f1(2) f2(1) f3(1)
複数の初期化文を静的中括弧で囲み、静的ブロックと呼ぶ 実際には複数の静的をまとめて記述します。 。これは、オブジェクトが最初に作成されたとき、またはクラスのフィールドに初めてアクセスされたときに、1 回だけ実行されます。
class Cup { Cup(int marker) { print("Cup(" + marker + ")"); } void f(int marker) { print("f(" + marker + ")"); } } class Cups { static Cup cup1; static Cup cup2; static { cup1 = new Cup(1); cup2 = new Cup(2); } Cups() { print("Cups()"); } } public class ExplicitStatic { public static void main(String[] args) { print("Inside main()"); Cups.cup1.f(99); // (1) } // static Cups cups1 = new Cups(); // (2) // static Cups cups2 = new Cups(); // (2) }
Inside main() Cup(1) Cup(2) f(99)
特に説明することはありません。これは単なる通常の初期化であり、順番に実行され、複数回実行できます。
class Mug { Mug(int marker) { print("Mug(" + marker + ")"); } void f(int marker) { print("f(" + marker + ")"); } } public class Mugs { Mug mug1; Mug mug2; { mug1 = new Mug(1); mug2 = new Mug(2); print("mug1 & mug2 initialized"); } Mugs() { print("Mugs()"); } Mugs(int i) { print("Mugs(int)"); } public static void main(String[] args) { print("Inside main()"); new Mugs(); print("new Mugs() completed"); new Mugs(1); print("new Mugs(1) completed"); } }
Inside main() Mug(1) Mug(2) mug1 & mug2 initialized Mugs() new Mugs() completed Mug(1) Mug(2) mug1 & mug2 initialized Mugs(int) new Mugs(1) completed