2

私は経験豊富なプログラムですが、これまで修正できなかったこの非常に奇妙な問題に遭遇しました。次のエラーが表示されます。

Exception in thread "Thread-0" java.lang.NoClassDefFoundError: rec/MiscIO
        at rec.RECTool.run(RECTool.java:264)
        at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: rec.MiscIO
        at java.net.URLClassLoaderrun(Unknown Source)
        at java.net.URLClassLoaderrun(Unknown Source)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 2 more

関連する行は次のとおりです。

File savedTo = MiscIO.copyJar(root);

私が理解していないのは、これが小さなアプリケーションであり、使用中のクラスが同じパッケージにあるということです。クラス名と内容以外は基本的に同じ設定。ただし、すべてではなく、一部のクラスでのみエラーが発生します。エラーが発生する前にこれらの行を使用しますが、これらは同じパッケージとすべてに含まれています。

Logger.log("Found new root: " + newRoots[i0]);
...
FileLocker locker = new FileLocker();
...
locker.lock(PathManager.getJar());

したがって、エラーのあるクラスの設定と基本的に同一の 3 つのクラスのうち、例外をスローするクラスは 1 つだけです。これは私には意味がありません。

クラスパスの変更や、jar コマンドを使用する代わりにメイン クラスを直接呼び出すなど、jar を実行するためにさまざまな引数を試しましたが、次のように役に立ちませんでした。

java -jar <jarname>
java -cp . <jarname>
java -cp <jarname> -jar <jarname>
java -cp <jarname> <main class here>

ただし、jar を解凍してプログラムを実行すると、エラーは発生しません。MiscIO の読み込みに失敗したか、静的初期化子の読み込みに失敗したことに依存している可能性があると思いましたが、どちらも私のコードにはありません。

また、Netbeans を使用して jar を生成しており、マニフェストが正しく設定されています。コード全体は次のとおりです。

    package rec;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.net.URL;

/**
 * @author Colby
 */
public class MiscIO {

    public static void copyJar(RandomAccessFile out) throws IOException {
        URL jarSource = RECTool.class.getProtectionDomain().getCodeSource().getLocation();

        InputStream in = null;
        try {
            in = jarSource.openStream();
            copyToRAF(in, out);

        } finally {
            if (in != null) {
                in.close();
            }
        }
    }

    public static File copyJar(File saveJarTo) throws IOException {
        URL jarSource = RECTool.class.getProtectionDomain().getCodeSource().getLocation();
        saveJarTo = new File(saveJarTo, "classlist.jar");

        InputStream in = null;
        OutputStream out = null;
        try {
            in = jarSource.openStream();
            out = new FileOutputStream(saveJarTo);
            copyStream(in, out);

        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
        return saveJarTo;
    }

    public static void copyStream(InputStream in, OutputStream out) throws IOException {
        int len;
        byte[] data = new byte[1024 * 8];
        while ((len = in.read(data)) != -1) {
            out.write(data, 0, len);
        }
        out.flush();
    }

    public static void copyToRAF(InputStream in, RandomAccessFile raf) throws IOException {
        raf.seek(0L);

        int len;
        byte[] data = new byte[1024 * 8];
        while ((len = in.read(data)) != -1) {
            raf.write(data, 0, len);
        }
    }

    public static void copyFile(File from, File to) throws IOException {
        FileInputStream in = null;
        FileOutputStream out = null;
        try {
            in = new FileInputStream(from);
            out = new FileOutputStream(to);
            copyStream(in, out);

        } finally {
            if (in != null) {
                in.close();
            }
            if (out != null) {
                out.close();
            }
        }
    }
}

最後に、自分のマシンにインストールされている Java のバージョンが競合している可能性があるか、まぐれによる x86/x64 の競合である可能性があると考えたため、すべての Java ディストリビューションをアンインストールし、1 つのアーキテクチャの最新のもののみをインストールしましたが、問題は残ります。このようなものを見た人はいますか?

4

3 に答える 3

2

あなたが言ったことに基づいて、最も可能性の高い説明は、JAR ファイルを間違って作成したということです。

実行しますjar -tvf <jarname>。MiscIO クラスが「rec/MiscIO.class」として表示されます。そうでない場合、クラスローダーはそれを見つけることができません。

(可能性が低い他の説明もあります。たとえば、カスタム クラスローダーや、以前にクラスのロードに失敗したクラスをロードしようとしている JVM が関係しています。)


記録としては、競合する Java バージョンがこれらの正確な症状の問題を引き起こすことはありません。(または、少なくとも、>>I<< どうやってそれができるのかわかりません ...)


あなたのコードは、それがより複雑で曖昧なものであったことを示しています...そしておそらくプラットフォームに依存しています。

于 2012-11-22T03:40:53.850 に答える
2

可能性が低い別の説明は、Thread Context ClassLoaderである可能性があります。

新しいスレッドで例外が発生することがわかったので、Context Class Loader が呼び出しスレッドと同じではない可能性があります。次のスタック オーバーフローの回答で説明されているように、新しいスレッドを作成するコードと、それが Class no found の同じクラスパスにあるかどうかによって異なります。

各クラスは、独自のクラスローダーを使用して他のクラスをロードします。したがって、ClassA.class が ClassB.class を参照する場合、ClassB は ClassA のクラスローダーのクラスパス上にあるか、またはその親である必要があります。

スレッド コンテキスト クラスローダーは、現在のスレッドの現在のクラスローダーです。ClassLoaderC のクラスからオブジェクトを作成し、ClassLoaderD が所有するスレッドに渡すことができます。この場合、独自のクラスローダでは利用できないリソースをロードしたい場合、オブジェクトは Thread.currentThread().getContextClassLoader() を直接使用する必要があります。

(抜粋: スレッドのコンテキストクラスローダと通常のクラスローダの違い

これを除外するために実行できるテストClass.forName("rec.MiscIO")は、アプリのメイン スレッドでそれが null ではないことをアサートすることです。

于 2012-11-22T03:50:55.583 に答える
2

おい、あなたのファイルロッカーは何をしますか?

1    FileLocker locker = new FileLocker();
2    locker.lock(PathManager.getJar());
3    File savedTo = MiscIO.copyJar(root);

行 [2] が を含む jar を排他的にロックする場合MiscIO.class、行 [3] は MiscIO クラスをロードできません。

于 2012-11-22T04:01:08.813 に答える