Javaで静的フィールドの初期化に失敗した場合のjava.lang.ExceptionInInitializerErrorを修正する

intermediate Java2026-05-04| Java 8以降、任意のOS(Windows/Linux/macOS)、JVMベースのプロジェクト(Maven、Gradle、Spring Boot、通常のJava)

Error Message

java.lang.ExceptionInInitializerError at com.example.MyClass.<clinit>(MyClass.java:15) Caused by: java.lang.NullPointerException
#java#静的初期化#イニシャライザ#クラスローダー#例外#デバッグ

エラーの内容

アプリが起動時に何もできないままクラッシュします。コンソールには以下が表示されます:

java.lang.ExceptionInInitializerError
	at com.example.MyClass.<clinit>(MyClass.java:15)
Caused by: java.lang.NullPointerException
	at com.example.MyClass.<clinit>(MyClass.java:12)

3つの手がかりが何が起きたかを教えてくれます:

  • ExceptionInInitializerError — JVMがクラスの読み込みを完了できませんでした。
  • <clinit> — クラス初期化子のことです。staticブロックまたはstaticフィールドの代入が失敗しています。
  • Caused by — 実際の問題です。ここではNullPointerExceptionですが、IllegalArgumentExceptionIOException、または独自コードからの例外など、何でも起こりえます。

なぜこのエラーが発生するのか

JVMがクラスを初めて読み込むとき、宣言順にすべてのstatic初期化子を実行します — インラインフィールドの代入が先で、次にstatic { ... }ブロックです。その中のいずれかが非チェック例外(またはError)をスローすると、JVMはそれをExceptionInInitializerErrorでラップし、そのクラスを永続的に壊れた状態としてマークします。

厄介なのはここからです:その後そのクラスを使おうとするたびに — catchブロックの中でさえ — NoClassDefFoundErrorがスローされます。すべてが1つの壊れた静的初期化子に遡る連鎖的な障害が発生します。連鎖ではなく、根本原因を修正してください。

よくある原因

  • 起動時にnullのシステムプロパティや環境変数を読み込み、すぐにそのメソッドを呼び出している
  • 一度も初期化されていないstaticオブジェクトのメソッドを呼び出している
  • ハードコードされた文字列(日付フォーマット、数値など)のパースが失敗している
  • クラスが最初に読み込まれるときに存在しないファイルやクラスパスリソースを読み込もうとしている
  • 2つのクラスが読み込み時にお互いのstaticフィールドを参照している(循環初期化)

手順ごとの修正方法

手順1 — スタックトレースの正しい部分を読む

先頭のExceptionInInitializerErrorの行は無視してください。Caused byまでスクロールし、ソースファイルの行番号を確認します。

Caused by: java.lang.NullPointerException
	at com.example.MyClass.<clinit>(MyClass.java:12)

MyClass.javaを開き、12行目に移動してください。そこに問題があります。

手順2 — 失敗しているstaticコードを特定する

ほとんどのケースは2つのパターンに当てはまります:

// パターン1: インラインstaticフィールド
public class MyClass {
    // System.getenv("DB_URL") はnullを返す可能性がある — nullに対して.trim()を呼ぶと即クラッシュ
    private static final String DB_URL = System.getenv("DB_URL").trim();
}

// パターン2: staticブロック
public class MyClass {
    private static final Connection conn;
    static {
        conn = DriverManager.getConnection(DB_URL); // DB_URLがnullの場合にスロー
    }
}

手順3 — nullまたはエラーのガード処理を追加する

値を使用する前に検証してください。わかりやすいエラーメッセージがあれば、後のデバッグで20分を節約できます:

public class MyClass {
    private static final String DB_URL;

    static {
        String url = System.getenv("DB_URL");
        if (url == null || url.isBlank()) {
            throw new IllegalStateException(
                "Environment variable DB_URL is not set. " +
                "Please configure it before starting the application."
            );
        }
        DB_URL = url.trim();
    }
}

staticブロックからIllegalStateExceptionをスローしてもExceptionInInitializerErrorは発生します。しかし今度はCaused byのメッセージに何が不足しているかが正確に示されます — 推測は不要です。

手順4 — 初期化をstaticコンテキストから移動させる

static初期化子は一度だけ、アプリが何もキャッチできる前に静かに実行されます。ファイルの読み込み、ネットワーク呼び出し、リソースの読み込みはそこに書くべきではありません。遅延初期化の方が安全です:

// 変更前: 読み込み時にスローされる可能性があるstatic初期化
public class ConfigLoader {
    private static final Properties props = loadProperties(); // main()の前にクラッシュが発生

    private static Properties loadProperties() {
        // config.propertiesを読み込む — 存在しない場合はIOExceptionをスロー
    }
}

// 変更後: 最初の使用時まで遅延し、エラーを明示的に処理する
public class ConfigLoader {
    private static Properties props;

    public static synchronized Properties getProps() {
        if (props == null) {
            try {
                props = loadProperties();
            } catch (IOException e) {
                throw new RuntimeException("Failed to load config.properties", e);
            }
        }
        return props;
    }
}

手順5 — 循環static依存関係を解消する

クラスAがB.PREFIXを参照し、クラスBがA.NAMEを参照しています。JVMが先にAを読み込むと、Bがまだ初期化されていないためB.PREFIXがnullになります。解決策は共有定数を持つ第3のクラスを作ることです:

// 問題のあるコード: AがBを読み込み、BがAを読み込む — どちらかがnullを見る
public class A {
    public static final String NAME = B.PREFIX + "_A";
}
public class B {
    public static final String PREFIX = A.NAME + "_B";
}

// 修正: Constantsに切り出す — 循環依存がなくなる
public class Constants {
    public static final String PREFIX = "APP";
}
public class A {
    public static final String NAME = Constants.PREFIX + "_A";
}
public class B {
    public static final String NAME = Constants.PREFIX + "_B";
}

手順6 — サードパーティクラスが失敗している場合

時として<clinit>が自分のコードではなくライブラリを指すことがあります。これは通常、設定ファイルやクラスパスリソースが不足していることを意味します。Caused byの連鎖全体を読んでください:

// 例: Log4jが設定ファイルを見つけられない
java.lang.ExceptionInInitializerError
	at org.apache.logging.log4j.LogManager.<clinit>(LogManager.java:...)
Caused by: java.lang.IllegalStateException: No log4j2 configuration file found

この場合の修正はlog4j2.xmlをクラスパスに置くことです — Javaコードを変更する必要はありません。

修正の確認

  • 再コンパイルして再実行してください。ExceptionInInitializerErrorが消えているはずです。
  • IllegalStateExceptionをスローするガード処理を追加した場合はテストしてください:環境変数を未設定にして、生のNPEではなく独自のメッセージが表示されることを確認します。
  • ユニットテストを実行してください。クラスの読み込みに静かに失敗していたテストが、今度はパスするか、読みやすいエラーメッセージを出すようになります。
# 修正後のスモークテスト
$ java -cp . com.example.Main
# 期待される結果: アプリが正常に起動し、ExceptionInInitializerErrorが出ない

# 空の環境変数でガード処理をテスト
$ DB_URL= java -cp . com.example.Main
# 期待される結果: IllegalStateException: Environment variable DB_URL is not set.

クイックリファレンス

  • 先頭行ではなくCaused byを読むExceptionInInitializerErrorはラッパーであり、実際の例外はその下にあります。
  • staticブロック内で環境変数やシステムプロパティのnullチェックを行う、メソッドを呼び出す前に。
  • ファイルI/Oやネットワーク呼び出しをstatic初期化子に書かない — リトライやキャッチができるメソッドに遅延させてください。
  • クラスの循環依存関係 — 共有定数を専用クラスに切り出してサイクルを断ち切ってください。
  • **最初の失敗後のNoClassDefFoundError**は後続の症状であり、別のバグではありません。最初に失敗したstatic初期化子を修正してください。

Related Error Notes