Jaka jest różnica między zależnościami czasu kompilacji i czasu wykonywania w języku Java? Jest to związane ze ścieżką klasową, ale czym się różnią?
Odpowiedzi:
Zależność w czasie kompilacji : potrzebujesz zależności w swoim, CLASSPATH
aby skompilować artefakt. Są tworzone, ponieważ masz jakieś „odniesienie” do zależności zakodowanej na stałe w swoim kodzie, na przykład wywołanie new
jakiejś klasy, rozszerzenie lub zaimplementowanie czegoś (bezpośrednio lub pośrednio) lub wywołanie metody przy użyciu reference.method()
notacji bezpośredniej .
Zależność czasu wykonywania : potrzebujesz zależności w swoim, CLASSPATH
aby uruchomić artefakt. Są tworzone, ponieważ wykonujesz kod, który uzyskuje dostęp do zależności (w zakodowany sposób, poprzez odbicie lub cokolwiek innego).
Chociaż zależność od czasu kompilacji zwykle implikuje zależność od czasu wykonywania, można mieć zależność tylko w czasie kompilacji. Jest to oparte na fakcie, że Java łączy zależności klas tylko przy pierwszym dostępie do tej klasy, więc jeśli nigdy nie uzyskasz dostępu do określonej klasy w czasie wykonywania, ponieważ ścieżka kodu nigdy nie jest przechodzona, Java zignoruje zarówno klasę, jak i jej zależności.
Przykład tego
W C.java (generuje klasę C.):
package dependencies;
public class C { }
W A.java (generuje klasę A.):
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());
}
}
}
W tym przypadku A
ma zależność w czasie kompilacji od C
through B
, ale będzie miała zależność czasu wykonywania od języka C tylko wtedy, gdy przekażesz niektóre parametry podczas wykonywania java dependencies.A
, ponieważ JVM będzie próbować rozwiązać B
zależność tylko od tego, C
kiedy zostanie wykonana B b = new B()
. Ta funkcja umożliwia udostępnienie w czasie wykonywania tylko zależności klas, których używasz w ścieżkach kodu, i zignorowanie zależności pozostałych klas w artefakcie.
Prostym przykładem jest spojrzenie na interfejs API, taki jak api serwletu. Aby skompilować serwlety, potrzebny jest plik servlet-api.jar, ale w czasie wykonywania kontener serwletu zapewnia implementację apletu serwletu, więc nie ma potrzeby dodawania pliku servlet-api.jar do ścieżki klasy środowiska wykonawczego.
Kompilator potrzebuje odpowiedniej ścieżki klasy, aby skompilować wywołania biblioteki (zależności czasu kompilacji)
JVM potrzebuje odpowiedniej ścieżki klas, aby załadować klasy w wywoływanej bibliotece (zależności środowiska wykonawczego).
Mogą się różnić na kilka sposobów:
1) jeśli twoja klasa C1 wywołuje klasę biblioteki L1, a L1 wywołuje klasę biblioteki L2, wówczas C1 ma zależność wykonawczą od L1 i L2, ale tylko zależność czasu kompilacji od L1.
2) jeśli twoja klasa C1 dynamicznie tworzy instancję interfejsu I1 za pomocą Class.forName () lub innego mechanizmu, a klasa implementująca dla interfejsu I1 to klasa L1, wówczas C1 ma zależność wykonawczą od I1 i L1, ale tylko zależność czasu kompilacji na I1.
Inne "pośrednie" zależności, które są takie same w czasie kompilacji i wykonywania:
3) Twoja klasa C1 rozszerza klasę biblioteki L1, a L1 implementuje interfejs I1 i rozszerza klasę biblioteki L2: C1 ma zależność czasu kompilacji od L1, L2 i I1.
4) Twoja klasa C1 ma metodę foo(I1 i1)
i metodę, bar(L1 l1)
gdzie I1 jest interfejsem, a L1 jest klasą, która przyjmuje parametr, którym jest interfejs I1: C1 ma zależność w czasie kompilacji od I1 i L1.
Zasadniczo, aby zrobić coś interesującego, Twoja klasa musi łączyć się z innymi klasami i interfejsami w ścieżce klas. Wykres klasy / interfejsu utworzony przez ten zestaw interfejsów biblioteki daje łańcuch zależności w czasie kompilacji. Implementacje bibliotek dają łańcuch zależności czasu wykonywania. Należy zauważyć, że łańcuch zależności czasu wykonywania jest zależny od czasu wykonania lub powolny: jeśli implementacja L1 czasami zależy od tworzenia instancji obiektu klasy L2, a instancja tej klasy jest tworzona tylko w jednym konkretnym scenariuszu, to nie ma zależności z wyjątkiem ten scenariusz.
W rzeczywistości Java nie łączy niczego w czasie kompilacji. Weryfikuje tylko składnię przy użyciu pasujących klas, które znajduje w CLASSPATH. Dopiero w czasie wykonywania wszystko zostaje złożone i wykonane w oparciu o CLASSPATH w tym czasie.
Zależności czasu kompilacji to tylko zależności (inne klasy), których używasz bezpośrednio w kompilowanej klasie. Zależności czasu wykonywania obejmują zarówno bezpośrednie, jak i pośrednie zależności klasy, którą uruchamiasz. W związku z tym zależności środowiska uruchomieniowego obejmują zależności zależności i wszelkie zależności odbicia, takie jak nazwy klas, które masz w pliku String
, ale są używane w Class#forName()
.
A
, B.jar with B extends A
i C.jar with, C extends B
to C.jar zależy od czasu kompilacji na A.jar, mimo że zależność C od A jest pośrednia.
W przypadku języka Java zależność od czasu kompilacji to zależność kodu źródłowego. Na przykład, jeśli klasa A wywołuje metodę z klasy B, to A jest zależne od B w czasie kompilacji, ponieważ A musi wiedzieć o B (typ B) do skompilowania. Sztuczka powinna być taka: skompilowany kod nie jest jeszcze kompletnym i wykonywalnym kodem. Zawiera wymienne adresy (symbole, metadane) dla źródeł, które nie zostały jeszcze skompilowane lub nie istnieją w zewnętrznych jarach. Podczas linkowania adresy te należy zastąpić aktualnymi adresami w pamięci. Aby to zrobić poprawnie należy stworzyć poprawne symbole / adresy. Można to zrobić z typem klasy (B). Uważam, że jest to główna zależność w czasie kompilacji.
Zależność środowiska wykonawczego jest bardziej związana z rzeczywistym przepływem kontroli. Uwzględnia rzeczywiste adresy pamięci. Jest to zależność, która występuje, gdy program jest uruchomiony. Potrzebujesz tutaj szczegółów klasy B, takich jak implementacje, a nie tylko informacji o typie. Jeśli klasa nie istnieje, otrzymasz RuntimeException i JVM zakończy działanie.
Obie zależności, ogólnie i nie powinny, płynąć w tym samym kierunku. Jest to jednak kwestia projektu OO.
W C ++ kompilacja jest nieco inna (nie tylko w czasie), ale ma też konsolidator. Myślę, że proces można by pomyśleć podobnie jak w Javie.