1. 序章

ClassNotFoundExceptionNoClassDefFoundErrorはどちらも、JVMがクラスパスで要求されたクラスを見つけることができない場合に発生します。 見覚えはありますが、これら2つの間にいくつかの主要な違いがあります。

このチュートリアルでは、それらが発生する理由とその解決策のいくつかについて説明します。

2. ClassNotFoundException

ClassNotFoundException は、アプリケーションが完全修飾名を使用してクラスを読み込もうとしたときに、クラスパスでその定義が見つからない場合に発生するチェック済みの例外です。

これは主に、 Class.forName() ClassLoader.loadClass()、または ClassLoader.findSystemClass()を使用してクラスをロードしようとしたときに発生します。 したがって、リフレクションを操作するときは、java.lang.ClassNotFoundExceptionに特に注意する必要があります。

たとえば、 ClassNotFoundException:を取得する必要な依存関係を追加せずに、JDBCドライバークラスをロードしてみましょう。

@Test(expected = ClassNotFoundException.class)
public void givenNoDrivers_whenLoadDriverClass_thenClassNotFoundException() 
  throws ClassNotFoundException {
      Class.forName("oracle.jdbc.driver.OracleDriver");
}

3. NoClassDefFoundError

NoClassDefFoundErrorは致命的なエラーです。 これは、JVMが次のことを試みているときにクラスの定義を見つけることができない場合に発生します。

  • newキーワードを使用してクラスをインスタンス化します
  • メソッド呼び出しでクラスをロードする

このエラーは、コンパイラがクラスを正常にコンパイルできたが、Javaランタイムがクラスファイルを見つけられなかった場合に発生します。 これは通常、静的ブロックの実行中またはクラスの静的フィールドの初期化中に例外が発生した場合に発生するため、クラスの初期化は失敗します。

問題を再現する簡単な方法の1つであるシナリオを考えてみましょう。 ClassWithInitErrors初期化は例外をスローします。 だから、私たちがのオブジェクトを作成しようとすると ClassWithInitErrors、 それは投げます ExceptionInInitializerError。

同じクラスを再度ロードしようとすると、 NoClassDefFoundError:が発生します。

public class ClassWithInitErrors {
    static int data = 1 / 0;
}
public class NoClassDefFoundErrorExample {
    public ClassWithInitErrors getClassWithInitErrors() {
        ClassWithInitErrors test;
        try {
            test = new ClassWithInitErrors();
        } catch (Throwable t) {
            System.out.println(t);
        }
        test = new ClassWithInitErrors();
        return test;
    }
}

このシナリオのテストケースを書いてみましょう。

@Test(expected = NoClassDefFoundError.class)
public void givenInitErrorInClass_whenloadClass_thenNoClassDefFoundError() {
 
    NoClassDefFoundErrorExample sample
     = new NoClassDefFoundErrorExample();
    sample.getClassWithInitErrors();
}

4. 解像度

これらの2つの問題を診断して修正するには、非常に時間がかかる場合があります。 両方の問題の主な理由は、実行時に(クラスパス内の)クラスファイルが使用できないことです。

これらのいずれかに対処するときに検討できるいくつかのアプローチを見てみましょう。

  1. そのクラスを含むクラスまたはjarがクラスパスで使用可能かどうかを確認する必要があります。 そうでない場合は、追加する必要があります
  2. アプリケーションのクラスパスで使用できる場合は、おそらくクラスパスがオーバーライドされています。 これを修正するには、アプリケーションで使用される正確なクラスパスを見つける必要があります
  3. また、アプリケーションが複数のクラスローダーを使用している場合、1つのクラスローダーによってロードされたクラスが他のクラスローダーによって使用できない場合があります。 トラブルシューティングをうまく行うには、クラスローダーがJavaでどのように機能するかを知ることが不可欠です。

5. 概要

これらの例外は両方とも、実行時にクラスを見つけることができないクラスパスとJavaランタイムに関連していますが、それらの違いに注意することが重要です。

Javaランタイムは、実行時にのみクラスをロードしようとしているときに ClassNotFoundException をスローし、名前は実行時に提供されました。 NoClassDefFoundErrorの場合、クラスはコンパイル時に存在していましたが、Javaランタイムは実行時にJavaクラスパスでそれを見つけることができませんでした。

いつものように、すべての例の完全なコードは、GitHubにあります。