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


90

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

回答:


78
  • コンパイル時の依存関係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上のコンパイル時の依存関係を持っていCB、しかし、実行したときに、いくつかのパラメータを渡す場合にのみCのランタイム依存性を持つことになりますjava dependencies.AJVMが唯一の解決しようとすると、B上の依存関係をC、それが実行になったときB b = new B()。この機能を使用すると、実行時にコードパスで使用するクラスの依存関係のみを提供し、アーティファクト内の残りのクラスの依存関係を無視できます。


1
これは今では非常に古い答えですが、JVMが最初からランタイム依存関係としてCを持たないのはどうしてですか?「Cへの参照があります。依存関係として追加するときが来ました」を認識できる場合、JVMはそれを認識してどこにあるかを知っているので、Cはすでに本質的に依存関係ではありませんか?
wearebob

@wearebob私が推測するように指定できたかもしれませんが、彼らはレイジーリンクの方が良いと判断しました。個人的には上記の理由で同意します。必要に応じてコードを使用できますが、コードを含めるように強制することはありません。必要がない場合はデプロイメント。これは、サードパーティのコードを扱うときに非常に便利です。
gpeche

ただし、jarをどこかにデプロイしている場合は、その依存関係をすべて含める必要があります。引数を使用して実行されるかどうかがわからないため(したがって、Cが使用されるかどうかもわかりません)、どちらの方法でもCを使用できるようにする必要があります。最初からクラスパスにCがないために、メモリ/時間がどのように節約されるのかわかりません。
wearebob

1
@wearebob JARは、そのすべての依存関係を含める必要はありません。そのため、ほとんどすべての重要なアプリケーションには、複数のJARを含む/ libディレクトリなどがあります。
gpeche

33

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


明確にするために(これは私を混乱させました)、mavenを使用して戦争を構築している場合、「servlet-api」は通常、「ランタイム」依存関係ではなく「提供された」依存関係であり、これにより、戦争に含まれることになります。私は正しいです。
xdhmoore 2016

2
「提供された」とは、コンパイル時に含めることを意味しますが、WARまたはその他の依存関係のコレクションにバンドルしないでください。'runtime'はその逆を行います(コンパイル時には使用できませんが、WARにパッケージ化されています)。
KC Baltz 2017

29

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

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のオブジェクトのインスタンス化に依存することがあり、そのクラスが特定のシナリオでのみインスタンス化される場合、以下以外の依存関係はありません。そのシナリオ。


1
例1のコンパイル時の依存関係はL1であるべきではありませんか?
BalusC 2010年

ありがとう、しかし実行時にクラスのロードはどのように機能しますか?コンパイル時に理解するのは簡単です。しかし、実行時に、バージョンの異なる2つのJarがある場合、どのように機能しますか?どちらを選びますか?
Kunal 2010年

1
デフォルトのクラスローダーがクラスパスを取得して順番にステップスルーすることは間違いないので、クラスパスに同じクラスを含む2つのjar(com.example.fooutils.Fooなど)がある場合は、次のjarが使用されます。クラスパスの最初です。そうしないと、あいまいさを示すエラーが発生します。ただし、クラスローダーに固有の詳細情報が必要な場合は、別の質問をする必要があります。
ジェイソンS

最初のケースでは、コンパイル時の依存関係もL2にあるはずです。つまり、文は次のようになります。1)クラスC1がライブラリクラスL1を呼び出し、L1がライブラリクラスL2を呼び出す場合、C1はL1とL2ですが、コンパイル時の依存関係はL1とL2のみです。これは、コンパイル時にJavaコンパイラがL1を検証するときと同様に、L1によって参照される他のすべてのクラスも検証します(Class.forName( "myclassname)などの動的依存関係を除く)...それ以外の場合は、どのように検証しますかコンパイルは正常に機能しています。そうでないと思われる場合は説明してください
Rajesh Goel 2016

1
いいえ。Javaでコンパイルとリンケージがどのように機能するかを読む必要があります。コンパイラが外部クラスを参照するときに気にするのは、そのクラスの使用方法、たとえばそのメソッドとフィールドが何であるかだけです。その外部クラスのメソッドで実際に何が起こるかは関係ありません。L1がL2を呼び出す場合、それはL1の実装の詳細であり、L1はすでに他の場所でコンパイルされています。
ジェイソンS

12

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


ロードタイムまでではありません...ランタイムはロードタイムとは異なります。
過剰交換

10

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


ありがとう、しかし実行時にクラスのロードはどのように機能しますか?コンパイル時に理解するのは簡単です。しかし、実行時に、バージョンの異なる2つのJarがある場合、どのように機能しますか?クラスパスに異なるクラスの複数のクラスがある場合、Class.forName()はどのクラスを取得しますか?
Kunal 2010年

もちろん名前に合ったもの。実際に「同じクラスの複数のバージョン」を意味する場合、それはクラスローダーに依存します。「最も近い」ものがロードされます。
BalusC 2010年

A.jar with A、B.jar with B extends A、C.jar withがあるC extends B場合、AへのCの依存関係は間接的ですが、C.jarはA.jarのコンパイル時間に依存すると思います。
gpeche 2010年

1
すべてのコンパイル時の依存関係の問題は、インターフェイスの依存関係です(インターフェイスがクラスのメソッドを介しているか、インターフェイスのメソッドを介しているか、クラスまたはインターフェイスである引数を含むメソッドを介しているか)
Jason S

2

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

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

両方の依存関係は、一般的に、そしてそうすべきではなく、同じ方向に流れます。ただし、これはオブジェクト指向設計の問題です。

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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.