Javaのコンパイル時と実行時の依存関係の違いは何ですか?クラスパスに関連していますが、どのように異なりますか?
回答:
コンパイル時の依存関係: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());
}
}
}
この場合、A
上のコンパイル時の依存関係を持っていC
てB
、しかし、実行したときに、いくつかのパラメータを渡す場合にのみCのランタイム依存性を持つことになりますjava dependencies.A
JVMが唯一の解決しようとすると、B
上の依存関係をC
、それが実行になったときB 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にはメソッドfoo(I1 i1)
とメソッドbar(L1 l1)
があり、I1はインターフェイスであり、L1はインターフェイスI1であるパラメーターを受け取るクラスです。C1はコンパイル時にI1とL1に依存します。
基本的に、何か面白いことをするには、クラスが他のクラスやクラスパス内のインターフェイスとインターフェイスする必要があります。そのライブラリインターフェイスのセットによって形成されるクラス/インターフェイスグラフは、コンパイル時の依存関係チェーンを生成します。ライブラリの実装により、実行時の依存関係チェーンが生成されます。ランタイム依存関係チェーンはランタイム依存またはフェイルスローであることに注意してください。L1の実装がクラスL2のオブジェクトのインスタンス化に依存することがあり、そのクラスが特定のシナリオでのみインスタンス化される場合、以下以外の依存関係はありません。そのシナリオ。
コンパイル時の依存関係は、コンパイルするクラスで直接使用する依存関係(他のクラス)のみです。ランタイムの依存関係は、実行しているクラスの直接および間接の両方の依存関係をカバーします。したがって、実行時の依存関係には、依存関係の依存関係とString
、にあるがで使用されるクラス名のようなリフレクションの依存関係が含まれClass#forName()
ます。
A
、B.jar with B extends A
、C.jar withがあるC extends B
場合、AへのCの依存関係は間接的ですが、C.jarはA.jarのコンパイル時間に依存すると思います。
Javaの場合、コンパイル時の依存関係はソースコードの依存関係です。たとえば、クラスAがクラスBからメソッドを呼び出す場合、AはコンパイルされるB(Bのタイプ)について知っている必要があるため、コンパイル時にAはBに依存します。ここでの秘訣は次のとおりです。コンパイルされたコードは、まだ完全で実行可能なコードではありません。これには、まだコンパイルされていないか、外部jarに存在するソースの置き換え可能なアドレス(シンボル、メタデータ)が含まれています。リンク中に、これらのアドレスをメモリ内の実際のアドレスに置き換える必要があります。それを正しく行うには、正しい記号/アドレスを作成する必要があります。そして、これはクラスのタイプ(B)で行うことができます。それがコンパイル時の主な依存関係だと思います。
ランタイムの依存関係は、実際の制御フローとより関連しています。実際のメモリアドレスが含まれます。これは、プログラムの実行時に発生する依存関係です。ここでは、タイプ情報だけでなく、実装などのクラスBの詳細が必要です。クラスが存在しない場合は、RuntimeExceptionが発生し、JVMが終了します。
両方の依存関係は、一般的に、そしてそうすべきではなく、同じ方向に流れます。ただし、これはオブジェクト指向設計の問題です。
C ++では、コンパイルは(ジャストインタイムではなく)少し異なりますが、リンカーもあります。したがって、プロセスはJavaに似ていると思われるかもしれません。