読者です 読者をやめる 読者になる 読者になる

プログラムdeタマゴ

nodamushiの著作物は、文章、画像、プログラムにかかわらず全てUnlicenseです

NoClassDefFoundErrorを回避する。

JAVA

NoClassDefFoundErrorは基本的に起こしてはいけないのだが、ライブラリがインストールされているかどうかに依存していてどうしても起きるかどうか分からないと言うこともある。それを回避する話です。

 NoClassDefFoundErrorが起こるのは読み込めないクラスを読み込もうとしたときに起こり、基本的に取得できない。
基本的には

  • staticメンバーの値や関数を呼び出したとき
  • クラスのインスタンスを作成しようとしたとき
  • クラスを(またはインターフェースを)拡張したクラスの読み込みをしたとき

この3つのどこかで起こる。
ソースにするとこんな感じ。以下のソースではコンパイルした後にNodaクラスファイルを削除しています。

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
    }
    Test(){
        n = null;//null代入はエラーにならない 
         
        n = new Noda();//インスタンスを作成するとエラーになる。
        System.out.println(Noda.mushi);//staticメンバーを読み込むとエラーになる。 
        new Class2();//これもエラーになる。
    }
    
    public Noda noda(Noda n){//呼び出さない限りはエラーにならない。
        return new Noda();
    }

    Noda n;//宣言ではエラーにならない
    public static class Class1 extends Noda{}//宣言ではエラーにならない(外部から呼び出されるとエラー)
    public class Class2 extends Noda{}//内部クラスも宣言ではエラーにならない
}

とにかく、メモリにアクセスしたり、メモリを作成したりするとエラーが起こる。
 
上のソースをクラスファイルがあってもなくてもエラーなしに終了したければ、メモリにアクセする前にアクセス可能かどうかのif文でくくってやればいい。
 
さて、そのアクセス可能かどうかの判定はどうすればいいのか。
これは実際に一回どこかでNodaクラスファイルを読み込んで、読み込めるかどうか調べておけばいい。
ただ、普通に読み込むとエラーになるので、ErrorではなくExceptionを発生させ、捕捉するという方法をとる。
 
で、そこで使うのが、ClassクラスのforName関数。
final public static boolean canusenoda;
static{
    boolean b=true;
    try{
        Class.forName("Noda");
    } catch (ClassNotFoundException e) {
        b =false;
    }
    canusenoda = b;
}

これを入れておけば後はcanusenodaで条件分岐すればいい。
(canusenodaの値が変わることはないのでfinal 宣言してある。)