実行時にJavaがJarファイルの依存関係を正確に探すのはいつですか?
それを実行する最初の段階で?
依存関係のあるクラスを初期化しようとすると?
それともいつでも?
実行時にJavaがJarファイルの依存関係を正確に探すのはいつですか?
それを実行する最初の段階で?
依存関係のあるクラスを初期化しようとすると?
それともいつでも?
.class ファイルがロードされる正確なタイミングは指定されていません。ご存じのとおり、main
メソッドの実行が開始される前であっても、クラスパス内のすべてのクラス ファイルがロードされる可能性があります。
Java が指定する唯一のことは、クラスが初期化されるときです。これは、クラスをロードすることとはまったく異なります。
Java言語仕様、§12.3は次のように述べています(私の強調):
この仕様により、言語のセマンティクスが尊重され、クラスまたはインターフェイスが初期化される前に完全に検証および準備され、リンク中に検出されたエラーは、エラーに関係するクラスまたはインターフェースへのリンクを必要とする可能性のあるアクションがプログラムによって実行されるプログラム内のポイントでスローされます。
たとえば、実装では、クラスまたはインターフェイス内の各シンボリック参照を、使用時にのみ個別に解決するか(レイジーまたはレイト解決)、クラスの検証中に一度に解決するか(静的解決)を選択できます。これは、一部の実装では、クラスまたはインターフェイスが初期化された後も解決プロセスが続行される可能性があることを意味します。
現在、仕様では、JVMはさまざまなことを実行できるとされていますが、明らかに、特定のJVMは特定の1つのことを実行します。Markoの答えは、「main
メソッドが実行を開始する前でも、クラスパス内のすべてのクラスファイルがロードされる可能性がある」と述べています。彼は正しいですが、実際には、JVMがそれを実行することはありません。
Sun JVMで実際に発生するのは、ロードが可能な限り遅くなることだと思います。クラスが初期化されるときはいつでも、それが参照するクラスをロードして検証する必要がありますが、実際に使用されるまでは初期化する必要はありません。これはあまり詳細で信頼できる答えではないことを理解しています。
私はそれがコンパイル時に起こると確信しています。すべての要件と依存関係を満たさないと、コードのコンパイル済みバージョンを取得できません。
コンパイル済みの jar の場合、次の 2 つのクラスを用意しました。
public class Test {
public static void main(String[] args) {
System.out.println("T1: Hello world");
Test2.greet();
}
}
と:
public class Test2 {
public static void greet() {
System.out.println("T2: Hello world");
}
}
エクスポートされたプロジェクトを実行する最初の試み:
$ java -jar test.jar
T1: Hello world
T2: Hello world
次に、jar ファイルから Test2.class を削除してから、再度実行します。
$ java -jar test-mod.jar
T1: Hello world
Exception in thread "main" java.lang.NoClassDefFoundError: Test2
at Test.main(Test.java:6)
Caused by: java.lang.ClassNotFoundException: Test2
at java.net.URLClassLoader$1.run(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)
... 1 more
T1 テストはパスし、NoClassDefFoundError 例外が発生しました。したがって、あなたの質問に答えると、依存関係は実行時にチェックされます。