ClassNotFoundException対NoClassDefFoundError
1.はじめに
JVMがクラスパス上で要求されたクラスを見つけることができない場合、
ClassNotFoundException
と
NoClassDefFoundError
の両方が発生します。それらはよく知られているように見えますが、これら2つの間にはいくつかの中心的な違いがあります。
このチュートリアルでは、それらが発生した理由とその解決方法について説明します。
2.
ClassNotFoundException
ClassNotFoundException
は、アプリケーションが完全修飾名でクラスをロードしようとしたときにクラスパスでその定義が見つからなかった場合に発生するチェック済みの例外です。
これは主に、
Class.forName()
、
ClassLoader.loadClass()
、または
ClassLoader.findSystemClass()
を使用してクラスをロードしようとしたときに発生します。そのため、リフレクションを使用している間は、
java.lang.ClassNotFoundException
に特に注意する必要があります。
たとえば、必要な依存関係を追加せずにJDBCドライバクラスをロードしようとすると、__ClassNotFoundExceptionが発生します。
@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つのクラスローダによってロードされたものは、他のクラスローダによって利用可能ではないかもしれません。
うまくトラブルシューティングするには、https://en.wikipedia.org/wiki/Java__Classloader[Javaでクラスローダーがどのように動作するのか]を知ることが不可欠です
5.まとめ
これらの例外はどちらもクラスパスと実行時にクラスを見つけることができないJavaランタイムに関連していますが、それらの違いに注意することが重要です。
実行時にのみクラスをロードしようとしているときにJavaランタイムが
ClassNotFoundException
をスローし、その名前が実行時に提供されました。
NoClassDefFoundErrorの場合、
クラスはコンパイル時に存在していましたが、Javaランタイムはランタイム中にJavaクラスパスでそれを見つけることができませんでした。
いつものように、すべての例の完全なコードはhttps://github.com/eugenp/tutorials/tree/master/core-java-lang[over on GitHub]にあります。