1

Java が最初のアクセス (新しいインスタンスの作成、静的メソッドまたは静的フィールドの呼び出し) でクラスをロードすることは知っていますが、この単純な例では、実行時に ClassPath にないいくつかのクラスを使用する jar ファイルを実行しようとしています。例外が発生する前に、(最初のアクセスでクラスをロードするため)静的ブロックとメインメソッドにメッセージを出力することを期待しています。しかし、「スレッド「メイン」の例外 java.lang.NoClassDefFoundError: com/example/DateAbstract」が表示され、何も出力されませんでした。これは、クラスまたはインターフェースが別のjarファイルにあるメインクラスで抽象クラスまたはインターフェースを使用したときに発生しました。

public class Driver {
static { System.out.println("I am first.[static block]"); }
public static void main(String[] args) {
    System.out.println("I am first.[ main method]");
    DateAbstract date = new CustomDate();
    System.out.println(date.sayDate());
}

私の別の瓶で:

public class CustomDate extends DateAbstract {
@Override
public String sayDate() {
    return new Date().toString();
}
public abstract class DateAbstract {
public abstract String sayDate();

}

このコードを使用して、実行時にクラスをクラスパスに追加すると。何も変わっていません。静的ブロックを実行する前に実行を取得しました。

public class Driver {
static {
    System.out.println("I am first.[static block]");
    try {
        URL url = new File("lib/DateApi.jar").toURI().toURL();
        URLClassLoader urlClassLoader = (URLClassLoader) URLClassLoader.getSystemClassLoader();
        Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
        method.setAccessible(true);
        method.invoke(urlClassLoader,url);
    } catch (Exception e) {
        e.printStackTrace();
    }

}
public static void main(String[] args) {
    System.out.println("I am first.[ main method]");
    DateAbstract date = new CustomDate();
    System.out.println(date.sayDate());
}

}

質問 : なぜこれが起こっているのか、どのように解決すればよいのですか?

4

1 に答える 1

1

Java クラスは最初のアクセス時にロードされるというのは正しくありません。これをクラスの初期化と混同しています。これは、初期static化子ブロックとフィールド初期化子の Java コードを実行することを意味します。ロードと検証は、より早い時期に行われる可能性があります。仕様は、この点に関して JVM にある程度の自由を提供します。

ここで重要な点は、mainメソッドが type のオブジェクトをインスタンス化し、CustomDateそれをコンパイル時の型の変数に格納してから、その変数DateAbstractを呼び出そうとするsayDate()ことです。このインスタンス化CustomDateと呼び出しの組み合わせには、その正しさ、つまりが subtype であるDateAbstract.sayDate()かどうかの検証が必要です。したがって、これら 2 つのクラスのロードは、検証時にすでに行われています。CustomDateDateAbstract

これが原因であることは簡単に確認できます。ローカル変数の型を に変更するdateCustomDate、インスタンス化された型とメソッド呼び出しのレシーバーの型が同じになるため、型をロードしなくても正しさを証明できるため、実際にインスタンス化を試みるまで延ばすことになりますCustomDate。したがって、メッセージが出力されます。

それでも、読み込み時間は実装固有の詳細です。別の JVM は、検証に必要ない場合でも、参照されたクラスを熱心にロードする可能性があります。遅延読み込みを確実にする唯一の安全な方法は、動的読み込みを使用することClass.forName(String)です。このように分離されたクラス内では、すべての型が再び通常どおり参照される可能性があることに注意してください。そのため、クラスパスを調整した後に一度動的ロードを行うと、コードの書き方やパフォーマンスに大きな影響はありません。もちろん、クラスパスを調整するコードとそれに依存するコードを同じクラス内に置くと、確実に機能しません。

于 2016-08-31T18:22:35.050 に答える