初期化順序
このレッスンでは、static 初期化子・インスタンス初期化子・コンストラクタが実行される順を追えるようになります。
まず予測してください
class Item {
static String tag = init("static field");
static { System.out.println("static block"); }
String name = init("instance field");
{ System.out.println("instance block"); }
Item(String n) { System.out.println("ctor " + n); }
}
new Item("A");
new Item("B");何行出力され、どんな順に並ぶでしょうか。
答えは4段階の順序
実際の出力は次のとおりです。
static field
static block
instance field
instance block
ctor A
instance field
instance block
ctor B読み取れることは3つあります。
- static のものが先 — static フィールドの初期化と static 初期化子は、最初のインスタンスが作られるより前に、クラスが読み込まれた時点で走ります
- static は1回だけ — インスタンスを2つ作っても
static fieldとstatic blockは1度しか出ません - インスタンス初期化子はコンストラクタの本体より先 — 毎回のインスタンス生成のたびに、フィールド初期化とインスタンス初期化子が走り、そのあとでコンストラクタの本体が動きます
書いた順もそのまま効く
もう1つ大事なのが、同じ段階の中ではソースに書いた順で実行される点です。static フィールドの初期化と static 初期化子が交互に並んでいれば、上から順に走ります。インスタンス側も同じです。試験では、初期化子をわざとフィールド宣言より上に書いて順序を入れ替えてくる問題が出ます。
親クラスがあるとさらに先に来る
継承が絡むと、順序の先頭に親クラスの初期化が入ります。全体では次の並びになります。
- 親クラスの static 初期化
- 子クラスの static 初期化
- 親クラスのインスタンス初期化子とコンストラクタ本体
- 子クラスのインスタンス初期化子とコンストラクタ本体
コンストラクタの先頭には暗黙の super() が入るので、子のコンストラクタ本体が動く前に親が完成している、と考えると自然に覚えられます。この続きは第6章で扱います。
演習で確かめます
上の Item を自分で組み立てて、出力が本当にこの順になるかを実行して確かめます。出力を予想してから提出してください。
要件
- static フィールド tag と static 初期化子を、この順で書くこと
- インスタンスフィールド name とインスタンス初期化子を、この順で書くこと
- trace は new Item(n) と new Item("second") をこの順で作り、文字列 done を返すこと
入出力例
trace("A") → "static field
static block
instance field
instance block
ctor A
instance field
instance block
ctor second
done"
trace("B") → "static field
static block
instance field
instance block
ctor B
instance field
instance block
ctor second
done"ヒント
編集 LuaGate編集部