Javaの「静的初期化ブロック(static initializer block)」は、クラスが初めて使われるタイミング(JVMに読み込まれるタイミング)で、自動的に1度だけ実行される特別なコードブロックのことです。
初心者の方にも本質を理解してもらうために、なるべく平易に、かつ詳しく説明していきます。
1.まずは「クラスがロードされる」とは何か?
Javaは必要になったクラスのバイトコード(.classファイル)をクラスローダという仕組みを通じて読み込み(ロード)ます。例えば、以下のようなタイミングでクラスが「初めて使われる」と判断され、ロードが行われます。
- そのクラスをインスタンス化しよう(
new クラス名(...)しよう)としたとき - そのクラスの静的メンバ(
staticなメソッド・フィールド)にアクセスしたとき - そのクラスが直接エントリーポイント(
mainメソッド)になっている場合(java Exampleで起動するなど)
このとき、JVMはクラスのバイトコードを読み込んだあと、「初期化処理」を一連の手順で実行します。その初期化処理の中で呼ばれるのが静的初期化ブロックです。
2.静的初期化ブロックの書き方
public class Example {
// 静的初期化ブロック
static {
System.out.println("静的初期化ブロックが呼ばれました。");
// ここに一度だけ行いたい設定や初期処理を書く
}
public static void main(String[] args) {
System.out.println("mainメソッドが呼ばれました。");
}
}
static { ... }で囲んだところが「静的初期化ブロック」です。- このブロック内の処理は、
Exampleクラスが初めて使われるときに1度だけ実行されます。 - その後、
mainが呼ばれて「mainメソッドが呼ばれました。」と表示されます。
3.なぜ「静的初期化ブロック」が存在するのか
3-1. 静的フィールドの複雑な初期化が必要な場合
- 単純に「
static int x = 10;」のように値を代入するだけなら、フィールドの定義時に直接書けます。 - しかし、何か複雑な計算や外部リソースへのアクセスを経て値を設定しなければならない場合、次のようなコードが必要になります。
public class Config {
private static final String CONFIG_PATH;
static {
// 例: 環境変数やシステムプロパティから設定パスを取得
String path = System.getenv("MY_CONFIG_PATH");
if (path == null) {
path = "/default/config.json";
}
CONFIG_PATH = path;
}
// CONFIG_PATH を使った処理...
}
- このように「staticフィールドに代入する前に、なにか特殊なロジックを走らせたい」というときに便利です。
3-2. 一度だけ行いたい前処理
- プログラム全体で「一度だけ行えば十分」な初期化を、コンストラクタではなく静的初期化ブロックに書くことがあります。
- 例: ログ設定の初期化、ドライバのロード、重いリソースの読み込み、など。
4.実行タイミングと注意点
4-1. 一度だけ実行される
- どんなに複数回
newしてインスタンスを作ろうと、クラスがロードされるのは基本的に最初の1回だけなので、そのときに静的初期化ブロックも1回だけ実行されます。 - インスタンスをたくさん作るたびに実行されるわけではありません。
4-2. 複数の静的初期化ブロックがある場合
- Javaでは、1つのクラスに複数の
static { ... }ブロックを書けます。 - その場合は、ソースコード上に書かれた順番に実行されます。
- ただし、複数に分けるよりは1つにまとめたほうが可読性が高いことが多いです。
4-3. 例外処理に注意(ExceptionInInitializerError)
- 静的初期化ブロック内でエラーやチェック例外(
IOExceptionなど)が起きると、ExceptionInInitializerErrorがスローされ、クラスの初期化が失敗します。 - 一度初期化に失敗したクラスは、それ以降プログラム内で使えなくなるので、エラーが発生する場合のハンドリングを注意する必要があります。
5.普通の初期化ブロック・コンストラクタとの違い
5-1. インスタンス初期化ブロック { ... }(static なし)
public class Example {
{
// インスタンス初期化ブロック
// インスタンスが new されるたびに呼ばれる
}
}
これはインスタンス化するときに実行されます(何度でも)。
5-2. コンストラクタ
public Example() {
// コンストラクタ
// new されたとき、このクラスのインスタンスを作るタイミングで呼ばれる
}
インスタンスを作るときに呼ばれます。
5-3. 静的初期化ブロック(static { ... })
static {
// クラスが読み込まれるタイミング(初回使用時)に1度だけ呼ばれる
}
- 上記2つとは違い、インスタンスを作らなくてもクラスがロードされるだけで呼ばれます。
- また、1回だけです。
6.実際の使用例と実行順序
public class InitializationDemo {
private static final String STATIC_FIELD;
static {
System.out.println("静的初期化ブロックが呼ばれました。");
// ここで STATIC_FIELD を複雑に設定できる
STATIC_FIELD = "初期値";
}
// インスタンス初期化ブロック
{
System.out.println("インスタンス初期化ブロックが呼ばれました。");
}
// コンストラクタ
public InitializationDemo() {
System.out.println("コンストラクタが呼ばれました。");
}
public static void main(String[] args) {
System.out.println("mainメソッドが呼ばれました。");
System.out.println("---------- インスタンスを new します ----------");
InitializationDemo demo = new InitializationDemo();
System.out.println("STATIC_FIELDの値 = " + STATIC_FIELD);
}
}
実行結果イメージ
静的初期化ブロックが呼ばれました。 mainメソッドが呼ばれました。 ---------- インスタンスを new します ---------- インスタンス初期化ブロックが呼ばれました。 コンストラクタが呼ばれました。 STATIC_FIELDの値 = 初期値
- クラスが初めて使われる(
mainを呼び出す)- 静的初期化ブロックが呼ばれる
mainメソッドが呼ばれるnew InitializationDemo()のとき- インスタンス初期化ブロックが呼ばれる
- コンストラクタが呼ばれる
- 最後に
STATIC_FIELDの値が表示される
