web-dev-qa-db-ja.com

コンパイル時間と実行時間の依存関係-Java

Javaでのコンパイル時と実行時の依存関係の違いは何ですか?クラスパスに関連していますが、どのように違いますか?

76
Kunal
  • コンパイル時の依存関係:アーティファクトをコンパイルするには、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());
        }
    }
}
_

この場合、ACからBにコンパイル時の依存関係がありますが、実行時にパラメーターを渡すとCにのみ実行時の依存関係があります。 _Java dependencies.A_、JVMはB b = new B()を実行するとBCへの依存関係のみを解決しようとするため。この機能を使用すると、コードパスで使用するクラスの依存関係のみを実行時に提供し、アーティファクト内の残りのクラスの依存関係を無視できます。

68
gpeche

簡単な例は、サーブレットAPIのようなAPIを調べることです。サーブレットをコンパイルするには、servlet-api.jarが必要ですが、実行時にサーブレットコンテナがサーブレットAPI実装を提供するため、サーブレットクラスAPIパスをランタイムクラスパスに追加する必要はありません。

30
Martin Algesten

コンパイラーは、ライブラリーへの呼び出しをコンパイルするために正しいクラスパスを必要とします(コンパイル時の依存関係)

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への依存。

基本的に、興味深いことを行うには、クラスはクラスパス内の他のクラスおよびインターフェイスとインターフェイスする必要があります。ライブラリのセットによって形成されるクラス/インターフェイスグラフインターフェイスはコンパイル時の依存性チェーンを生成します。ライブラリimplementationsは実行時の依存性チェーンを生成します。時間依存性チェーンは実行時依存またはフェールスローです:L1の実装がクラスL2のオブジェクトのインスタンス化に依存することがあり、そのクラスが特定のシナリオでのみインスタンス化される場合、そのシナリオを除いて依存関係はありません。

25
Jason S

Javaは実際にはコンパイル時に何もリンクしません。 CLASSPATHで見つかった一致するクラスを使用して構文のみを検証します。その時点ですべてがまとめられ、CLASSPATHに基づいて実行されるのは、実行時までではありません。

11
JOTN

コンパイル時の依存関係は、コンパイルするクラスで直接を使用する依存関係(他のクラス)のみです。ランタイム依存関係は、実行中のクラスの直接依存関係と間接依存関係の両方をカバーしています。したがって、ランタイム依存関係には、依存関係の依存関係と、StringにあるがClass#forName()で使用されるクラス名のようなリフレクション依存関係が含まれます。

10
BalusC

Javaの場合、コンパイル時の依存関係はソースコードの依存関係です。たとえば、クラスAがクラスBからメソッドを呼び出す場合、AはコンパイルされるB(Bのタイプ)を知る必要があるため、コンパイル時にAはBに依存します。ここでのコツは次のとおりです。コンパイルされたコードはまだ完全で実行可能なコードではありません。まだコンパイルされていない、または外部jarに存在しないソースの置換可能なアドレス(シンボル、メタデータ)が含まれます。リンク中、これらのアドレスはメモリ内の実際のアドレスに置き換える必要があります。適切に行うには、正しいシンボル/アドレスを作成する必要があります。そして、これはクラスのタイプ(B)で行うことができます。これがコンパイル時の主な依存関係だと思います。

ランタイム依存性は、実際の制御フローとより関連しています。実際のメモリアドレスが含まれます。これは、プログラムの実行時にある依存関係です。ここでは、タイプ情報だけでなく、実装のようなクラスBの詳細が必要です。クラスが存在しない場合、RuntimeExceptionが発生し、JVMが終了します。

通常、両方の依存関係は同じ方向に流れます。これはOO designの問題です。

C++では、コンパイルは少し異なります(ジャストインタイムではありません)が、リンカーもあります。したがって、プロセスはJavaに似ていると思われるかもしれません。

1
stdout