Javaのコンパイル時と実行時の依存関係の違いは何ですか? クラスパスに関連していますが、それらはどのように違うのでしょうか?
7 に答える
コンパイル時の依存関係
CLASSPATH:アーティファクトをコンパイルするには、依存関係が必要です。それらは、コードにハードコーディングされた依存関係への何らかの「参照」があるために生成されます。たとえば、newクラスの呼び出し、何かの拡張または実装 (直接的または間接的)、または直接reference.method()表記を使用したメソッド呼び出しなどです。実行時の依存関係
CLASSPATH:アーティファクトを実行するには、依存関係が必要です。それらは、依存関係にアクセスするコードを実行するために生成されます (ハードコードされた方法またはリフレクションなどを介して)。
通常、コンパイル時の依存関係は実行時の依存関係を意味しますが、コンパイル時のみの依存関係を持つこともできます。これは、Java がそのクラスへの最初のアクセス時にのみクラスの依存関係をリンクするという事実に基づいているため、コード パスが決して走査されないために実行時に特定のクラスにアクセスしない場合、Java はクラスとその依存関係の両方を無視します。
この例
C.java (C.class を生成) の場合:
package dependencies;
public class C { }
A.java (A.class を生成) の場合:
package dependencies;
public class A {
public static class B {
public String toString() {
C c = new C();
return c.toString();
}
}
public static void main(String[] args) {
if (args.length > 0) {
B b = new B();
System.out.println(b.toString());
}
}
}
この場合、はthroughにAコンパイル時の依存関係を持ちますが、実行時にいくつかのパラメーターを渡す場合にのみ C に実行時の依存関係があります。これは、JVM が実行時にのみの依存関係を解決しようとするためです。 . この機能により、コード パスで使用するクラスの依存関係のみを実行時に提供し、アーティファクト内の残りのクラスの依存関係を無視することができます。CBjava dependencies.ABCB b = new B()
簡単な例は、サーブレットAPIのようなAPIを調べることです。サーブレットをコンパイルするには、servlet-api.jarが必要ですが、実行時にサーブレットコンテナがサーブレットAPI実装を提供するため、実行時クラスパスにservlet-api.jarを追加する必要はありません。
コンパイラは、ライブラリへの呼び出しをコンパイルするために正しいクラスパスを必要とします (コンパイル時の依存関係)
JVM は、呼び出しているライブラリ (実行時の依存関係) にクラスをロードするために、正しいクラスパスを必要とします。
それらはいくつかの点で異なる場合があります。
1) クラス C1 がライブラリ クラス L1 を呼び出し、L1 がライブラリ クラス L2 を呼び出す場合、C1 は L1 および L2 に対して実行時依存性を持ちますが、L1 に対するコンパイル時依存性のみを持ちます。
2) クラス C1 が Class.forName() またはその他のメカニズムを使用してインターフェイス I1 を動的にインスタンス化し、インターフェイス I1 の実装クラスがクラス L1 である場合、C1 は I1 および L1 に対して実行時依存性を持ちますが、コンパイル時の依存性のみを持ちます。 I1で。
コンパイル時と実行時で同じ他の「間接的な」依存関係:
3) クラス C1 はライブラリ クラス L1 を拡張し、L1 はインターフェイス I1 を実装し、ライブラリ クラス L2 を拡張します。C1 は、コンパイル時に L1、L2、および I1 に依存します。
4) クラス C1 には、I1 がインターフェイスであり、L1 がインターフェイス I1 であるパラメーターを受け取るクラスであるメソッドfoo(I1 i1)とメソッドbar(L1 l1)があります。C1 は、コンパイル時に I1 および L1 に依存します。
基本的に、興味深いことを行うには、クラスはクラスパス内の他のクラスおよびインターフェースとインターフェースする必要があります。そのライブラリインターフェイスのセットによって形成されるクラス/インターフェイス グラフは、コンパイル時の依存関係チェーンを生成します。ライブラリの実装により、実行時の依存関係チェーンが生成されます。実行時の依存関係チェーンは実行時依存またはフェイルスローであることに注意してください。L1 の実装がクラス L2 のオブジェクトのインスタンス化に依存する場合があり、そのクラスが 1 つの特定のシナリオでのみインスタンス化される場合、次の場合を除いて依存関係はありません。そのシナリオ。
Java は実際にはコンパイル時に何もリンクしません。CLASSPATH で見つかった一致するクラスを使用して構文を検証するだけです。その時点での CLASSPATH に基づいてすべてがまとめられて実行されるのは、実行時までではありません。
コンパイル時の依存関係は、コンパイルしているクラスで直接使用する依存関係 (他のクラス) のみです。ランタイムの依存関係は、実行中のクラスの直接的および間接的な依存関係の両方をカバーしています。Stringしたがって、実行時の依存関係には、依存関係の依存関係と、 にあるが で使用されるクラス名などのリフレクション依存関係が含まれますClass#forName()。
Java の場合、コンパイル時の依存関係はソース コードの依存関係です。たとえば、クラス A がクラス B のメソッドを呼び出す場合、A はコンパイル時に B (B の型) を知っている必要があるため、コンパイル時に A は B に依存します。ここでの秘訣は次のとおりです。コンパイルされたコードは、まだ完全で実行可能なコードではありません。これには、まだコンパイルされていない、または外部 jar に存在するソースの置き換え可能なアドレス (シンボル、メタデータ) が含まれています。リンク中に、これらのアドレスをメモリ内の実際のアドレスに置き換える必要があります。正しく行うには、正しいシンボル/アドレスを作成する必要があります。これは、クラスの型 (B) を使用して実行できます。それがコンパイル時の主な依存関係だと思います。
ランタイムの依存関係は、実際の制御フローに関連しています。実際のメモリアドレスが含まれます。これは、プログラムの実行中に存在する依存関係です。ここでは、型情報だけでなく、実装などのクラス B の詳細が必要です。クラスが存在しない場合は、RuntimeException が発生し、JVM は終了します。
両方の依存関係は、一般的に同じ方向に流れるべきではありませんが、同じ方向に流れます。ただし、これはオブジェクト指向の設計の問題です。
C++ では、コンパイルは少し異なりますが (ジャストインタイムではありません)、リンカーもあります。したがって、プロセスは Java に似ていると思われるかもしれません。