ClassNotFoundException vs NoClassDefFoundError
1. 前書き
ClassNotFoundExceptionとNoClassDefFoundErrorはどちらも、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つの問題を診断して修正するのに非常に時間がかかる場合があります。 両方の問題の主な理由は、実行時にクラスファイル(クラスパス内)が使用できないことです。
これらのいずれかに対処するときに検討できるいくつかのアプローチを見てみましょう。
-
そのクラスを含むクラスまたはjarがクラスパスで使用可能かどうかを確認する必要があります。 そうでない場合は、追加する必要があります
-
アプリケーションのクラスパスで利用できる場合は、おそらくクラスパスが上書きされています。 これを修正するには、アプリケーションで使用されている正確なクラスパスを見つける必要があります
-
また、アプリケーションが複数のクラスローダーを使用している場合、1つのクラスローダーによってロードされたクラスが他のクラスローダーによって使用できない場合があります。 トラブルシューティングを適切に行うには、how classloaders work in Javaを知ることが不可欠です。
5. 概要
これらの例外は両方とも、実行時にクラスを見つけることができないクラスパスとJavaランタイムに関連していますが、それらの違いに注意することが重要です。
Javaランタイムは、実行時にのみクラスをロードしようとしているときにClassNotFoundExceptionをスローし、名前は実行時に提供されました。 NoClassDefFoundError, theクラスの場合、コンパイル時に存在していましたが、Javaランタイムは実行時にJavaクラスパスでそれを見つけることができませんでした。
いつものように、すべての例の完全なコードはover on GitHubにあります。