2010-11-24 7 views

Antwort

18

Der Compiler benötigt die richtige Classpath, um Anrufe zu einer Bibliothek zu kompilieren (Zeitabhängigkeiten kompilieren)

Die JVM braucht die richtige Classpath, um die Klassen in der Bibliothek zu laden, die Sie anrufen (Runtime-Abhängigkeiten).

Sie können in ein paar Möglichkeiten unterscheiden:

1), wenn Ihre Klasse C1 ruft L1 Bibliothek Klasse und L1 ruft Bibliothek Klasse L2, dann hat C1 eine Laufzeit Abhängigkeit von L1 und L2, sondern nur ein Kompiliere Zeitabhängigkeit von L1.

2) Wenn Ihre Klasse C1 dynamisch ein Interface I1 mit Class.forName() oder einem anderen Mechanismus instanziiert, und die implementierende Klasse für Interface I1 ist Klasse L1, dann hat C1 eine Laufzeitabhängigkeit von I1 und L1, aber nur eine Kompilierzeitabhängigkeit von I1.

Andere „indirekte“ Abhängigkeiten, die das gleiche für Compile-Zeit und Laufzeit sind:

3) Klasse C1 erstreckt Bibliothek Klasse L1 und L1 implementiert Schnittstelle I1 und erweitert Bibliothek L2 Klasse: C1 a hat Kompilierzeitabhängigkeit von L1, L2 und I1.

4) Ihre Klasse C1 hat eine Methode foo(I1 i1) und eine Methode bar(L1 l1), wobei I1 eine Schnittstelle ist und L1 eine Klasse ist, die einen Parameter der Schnittstelle I1 akzeptiert: C1 hat eine Kompilierzeitabhängigkeit von I1 und L1.

Um etwas Interessantes zu tun, muss Ihre Klasse grundsätzlich mit anderen Klassen und Schnittstellen im Klassenpfad kommunizieren. Das Klassen-/Interface-Diagramm, das von diesem Satz von Interfaces gebildet wird, ergibt die Kompilierzeit-Abhängigkeitskette. Die Bibliothek Implementierungen ergibt die Laufzeitabhängigkeitskette. Beachten Sie, dass die Laufzeitabhängigkeitskette laufzeitabhängig oder fehlgeschlagen-langsam ist: Wenn die Implementierung von L1 manchmal von der Instanziierung eines Objekts der Klasse L2 abhängt und diese Klasse nur in einem bestimmten Szenario instanziiert wird, gibt es keine Abhängigkeit außer in diesem Szenario.

+0

Sollte die Compiletime-Abhängigkeit in Beispiel 1 nicht L1 sein? – BalusC

+0

yup, ein paar Tippfehler behoben. –

+0

Danke, aber wie funktioniert das Laden von Klassen zur Laufzeit? Zur Kompilierzeit ist es einfach zu verstehen. Aber zur Laufzeit, wie es sich verhält, wenn ich zwei Jars verschiedener Versionen habe? Welchen wird es auswählen? – Kunal

10

Java verbindet tatsächlich nichts zur Kompilierzeit. Es überprüft nur die Syntax anhand der übereinstimmenden Klassen, die im CLASSPATH gefunden werden. Erst zur Laufzeit wird alles auf der Grundlage des CLASSPATH zusammengestellt und ausgeführt.

9

Compiletime-Abhängigkeiten sind nur die Abhängigkeiten (andere Klassen), die Sie direkt in der Klasse verwenden, die Sie kompilieren. Laufzeitabhängigkeiten umfassen sowohl die direkten als auch die indirekten Abhängigkeiten der Klasse, die Sie ausführen. Laufzeitabhängigkeiten enthalten also Abhängigkeiten von Abhängigkeiten und alle Reflexionsabhängigkeiten wie Klassennamen, die Sie in einer String haben, aber in Class#forName() verwendet werden.

+0

Danke, aber wie das Laden der Klasse zur Laufzeit? Zur Kompilierzeit ist es leicht zu verstehen. Aber zur Laufzeit, wie es sich verhält, wenn ich zwei Jars verschiedener Versionen habe? Welche Klasse würde Class.forName() im Fall von mehrere Klassen von verschiedenen Klassen in einem Klassenpfad? – Kunal

+0

Der Name stimmt natürlich mit dem Namen überein. Wenn Sie * eigentlich * mehrere Versionen der gleichen Klasse meinen, dann kommt es auf den Klassenlader an. Der "nächste" wird geladen. – BalusC

+0

Nun, ich denke, wenn Sie A.jar mit 'A', B.jar mit' B extends A' und C.jar mit 'C extends B' haben, dann hängt C.jar von der Kompilierzeit auf A.jar ab, obwohl C-abhängig ist auf A ist indirekt. – gpeche

22

Ein einfaches Beispiel ist es, eine API wie die Servlet-API zu betrachten. Damit die Servlets kompiliert werden können, benötigen Sie die servlet-api.jar, aber zur Laufzeit stellt der Servlet-Container eine Servlet-API-Implementierung bereit, sodass Sie Ihrem Laufzeitklassenpfad keine servlet-api.jar hinzufügen müssen.

+0

Zur Klarstellung (this verwirrt mich), wenn Sie Maven verwenden und einen Krieg bauen, ist "Servlet-API" normalerweise eine "bereitgestellte" Abhängigkeit anstelle einer "Laufzeit" Abhängigkeit, die es veranlassen würde, im Krieg eingeschlossen zu werden, wenn ich richtig bin. – xdhmoore

+1

"bereitgestellt" bedeutet, dass sie zur Kompilierungszeit eingeschlossen werden, aber nicht in der WAR- oder anderen Sammlung von Abhängigkeiten gebündelt werden. 'runtime' macht das Gegenteil (nicht verfügbar bei der Kompilierung, aber verpackt mit der WAR). –

50
  • Compile-Zeitabhängigkeit: Sie müssen die Abhängigkeit in Ihrem CLASSPATH Ihren Artefakt kompilieren. Sie werden erzeugt, weil Sie eine Art "Referenz" zu der in Ihrem Code fest codierten Abhängigkeit haben, z. B. den Aufruf einer Klasse für new, das Erweitern oder Implementieren von etwas (entweder direkt oder indirekt) oder einen Methodenaufruf unter Verwendung der direkten Notation reference.method().

  • Laufzeitabhängigkeit: Sie benötigen die Abhängigkeit in Ihrem CLASSPATH, um Ihr Artefakt auszuführen. Sie werden erzeugt, weil Sie Code ausführen, der auf die Abhängigkeit zugreift (entweder auf eine fest codierte Weise oder durch Reflexion oder was auch immer).

Obwohl Kompilierung-Abhängigkeit in der Regel Laufzeitabhängigkeit bedeutet, können Sie einen Compiler-nur-Abhängigkeit haben. Dies basiert auf der Tatsache, dass Java Klassenabhängigkeiten nur beim ersten Zugriff auf diese Klasse verknüpft. Wenn Sie also niemals zur Laufzeit auf eine bestimmte Klasse zugreifen, weil ein Codepfad nie durchlaufen wird, ignoriert Java sowohl die Klasse als auch ihre Abhängigkeiten.

Beispiel dieser

In C.java (erzeugt C.class):

package dependencies; 
public class C { } 

In A.java (erzeugt 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()); 
     } 
    } 
} 

In diesem Fall , A hat eine Kompilierungszeitabhängigkeit von C bis B, aber es wird nur eine Laufzeitabhängigkeit von C haben, wenn Sie bei der Ausführung voneinige Parameter übergeben, da die JVM nur versuchen wird, Bs Abhängigkeit von C zu lösen, wenn sie B b = new B() ausführen soll. Mit dieser Funktion können Sie zur Laufzeit nur die Abhängigkeiten der Klassen angeben, die Sie in Ihren Codepfaden verwenden, und die Abhängigkeiten der übrigen Klassen im Artefakt ignorieren.

1

Bei Java ist die Abhängigkeit der Kompilierzeit abhängig von der Abhängigkeit Ihres Quellcodes. Wenn zum Beispiel Klasse A eine Methode von Klasse B aufruft, dann ist A zur Kompilierzeit von B abhängig, da A über B (Typ von B) Bescheid wissen muss, um kompiliert zu werden. Der Trick dabei sollte sein: Kompilierter Code ist noch kein vollständiger und ausführbarer Code. Es enthält austauschbare Adressen (Symbole, Metadaten) für die Quellen, die noch nicht in externen jars kompiliert oder vorhanden sind. Während des Verknüpfens müssen diese Adressen durch tatsächliche Adressen im Speicher ersetzt werden. Um es richtig zu machen, sollten korrekte Symbole/Adressen erstellt werden. Und das kann mit dem Typ der Klasse (B) gemacht werden. Ich glaube, das ist die Hauptabhängigkeit zur Kompilierzeit.

Die Laufzeitabhängigkeit hängt mehr mit dem tatsächlichen Ablauf der Steuerung zusammen. Es fragt tatsächliche Speicheradressen ab. Es ist eine Abhängigkeit, die Sie haben, wenn Ihr Programm läuft. Sie benötigen hier Informationen zur Klasse B wie Implementierungen, nicht nur die Typinformationen. Wenn die Klasse nicht existiert, erhalten Sie RuntimeException und JVM wird beendet.

Beide Abhängigkeiten, in der Regel und nicht in der gleichen Richtung. Dies ist jedoch eine Frage des OO-Designs.

In C++ ist die Kompilierung ein bisschen anders (nicht Just-in-Time), aber es hat auch einen Linker. Der Prozess könnte also ähnlich wie Java aussehen.