static フィールドの初期化順序
宣言順に初期化されます。static初期化ブロックとインスタンス初期化ブロックの違いも扱います。
なぜエラーが出ないのか
出力: 1
(エラーなし)- javacは何も報告しません。文法として正しいためです
出力: 1- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 public class Main { 2 static int a = Main.b + 1; 3 static int b = 2; 4 public static void main(String[] args) { 5 System.out.println(a ); ^ 6 } 7 }
出力: 1期待: 3 (コンパイルは通っています)
staticフィールドは書いた順に初期化されます。まだ0のbを使ってaが計算されます。
直し方: a を b にします。
広告
広告スロット(未設定)
パターン2
1 public class Main { 2 static int count; 3 { count = 10; } ^ 4 public static void main(String[] args) { 5 System.out.println(count); 6 } 7 }
出力: 0期待: 10 (コンパイルは通っています)
staticを付けない初期化ブロックはインスタンス生成時にしか動きません。mainからは実行されません。
直し方: (空) を static にします。
パターン3
1 public class Main { 2 static final int LIMIT; 3 { LIMIT = 5; } ^ 4 public static void main(String[] args) { 5 System.out.println(LIMIT); 6 } 7 }
Main.java:3: error: cannot assign a value to final variable LIMIT
static finalは宣言時かstatic初期化ブロックでのみ代入できます。インスタンス初期化ブロックからの代入は許されません。
直し方: (空) を static にします。
パターン4
1 public class Main { 2 static int x; 3 static { x = 1; } 4 static { x = 1 ; } ^ 5 public static void main(String[] args) { 6 System.out.println(x); 7 } 8 }
出力: 1期待: 2 (コンパイルは通っています)
複数のstaticブロックは宣言順に実行されます。後のブロックが前の結果を上書きしていないか確認します。
直し方: = 1 を += 1 にします。
パターン5
1 public class Main { 2 static int a = b + 1; ^ 3 static int b = 2; 4 public static void main(String[] args) { 5 System.out.println(a); 6 } 7 }
Main.java:2: error: illegal forward reference
まだ宣言されていないフィールドを単純名で参照すると、前方参照としてコンパイルエラーになります。クラス名で修飾すると許可されますが、値はまだ初期化前の0のままです。
直し方: b を Main.b にします。
よくある誤解
宣言の順番を入れ替えるだけで挙動が変わります。しかもエラーは出ません。
実務での勘所
staticフィールドやstatic初期化ブロックが実行されるタイミングは、プログラム起動時でもクラスを読み込んだ時点でもなく、そのクラスが実際に「能動的に使われた」瞬間(最初のインスタンス生成、最初のstaticメソッド呼び出しなど)です。この遅延初期化の仕組みのため、あるクラスを一度もnewしたり呼び出したりしていなければ、そのクラスのstaticブロックはプログラムが終了するまで一度も実行されません。「読み込まれているはずなのにログが出ない」という現象の多くは、このタイミングのずれが原因です。