Javaのクラス初期化順序(class initialization order)は、フィールド、ブロック、およびコンストラクタが初期化される順序を定義します。簡単に言うと:
class Parent { static { System.out.println("Parent static"); } { System.out.println("Parent init"); } Parent() { System.out.println("Parent constructor"); } } class Child extends Parent { static { System.out.println("Child static"); } { System.out.println("Child init"); } Child() { System.out.println("Child constructor"); } } // new Child() -> どの順序?
出力は:
まず親クラスの静的ブロックが実行され、その後子クラスの静的ブロック、最後に非静的ブロックとコンストラクタが継承順に実行されます。
質問: クラス内の静的イニシャライザはいつ実行されるのか?
class Ex {
static { System.out.println("static"); }
}
— 最初のインスタンス作成時か、任意の静的メソッド/フィールドの初回アクセス時か?
答え: 静的イニシャライザは、クラスへの初回アクセス時(いかなる静的メンバーへのアクセスを含む)に実行され、インスタンス作成時だけではありません。
物語
銀行アプリケーションでは、静的接続プールが静的ブロックで初期化されましたが、最初のインスタンス作成前に静的メソッドにアクセスしたため、接続は作成されていませんでした。これにより、負荷テスト中にNullPointerExceptionが発生しました。
物語
ログサービスは、親クラスのコンストラクタが呼ばれた後に初期化される非静的フィールドに依存していました。ログはnullロガーに書き込まれ、重要なエラーメッセージが失われました。
物語
初期値を持つ新しいフィールドを追加する際、開発者はイニシャライゼーションブロックの後に配置しました。ブロックはまだ初期化されていないフィールドを使用しようとしたため、アプリケーションの起動時にエラーが発生し、原因を見つけるのが困難でした。