Qual è la differenza tra il tempo di compilazione e le dipendenze del tempo di esecuzione in Java? È correlato al percorso di classe, ma in cosa differiscono?
Risposte:
In fase di compilazione delle dipendenze : è necessario la dipendenza nella vostra CLASSPATH
per compilare il manufatto. Vengono prodotti perché si dispone di una sorta di "riferimento" alla dipendenza codificata nel codice, come la richiesta new
di una classe, l'estensione o l'implementazione di qualcosa (direttamente o indirettamente) o una chiamata al metodo che utilizza la reference.method()
notazione diretta .
Dipendenza di runtime : è necessaria la dipendenza nel proprio CLASSPATH
per eseguire il proprio artefatto. Vengono prodotti perché esegui codice che accede alla dipendenza (in modo hardcoded o tramite reflection o altro).
Sebbene la dipendenza in fase di compilazione in genere implichi una dipendenza in fase di esecuzione, è possibile avere una dipendenza solo in fase di compilazione. Ciò si basa sul fatto che Java collega solo le dipendenze della classe al primo accesso a quella classe, quindi se non si accede mai a una particolare classe in fase di esecuzione perché un percorso del codice non viene mai attraversato, Java ignorerà sia la classe che le sue dipendenze.
Esempio di questo
In C.java (genera C.class):
package dependencies;
public class C { }
In A.java (genera 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 questo caso, A
ha una dipendenza in fase di compilazione da C
through B
, ma avrà una dipendenza in fase di esecuzione da C solo se si passano alcuni parametri durante l'esecuzione java dependencies.A
, poiché la JVM proverà a risolvere B
la dipendenza solo da C
quando deve essere eseguita B b = new B()
. Questa funzionalità consente di fornire in fase di esecuzione solo le dipendenze delle classi utilizzate nei percorsi del codice e di ignorare le dipendenze del resto delle classi nell'artefatto.
Un semplice esempio è guardare un'api come l'api servlet. Per compilare i servlet, è necessario servlet-api.jar, ma in fase di runtime il contenitore servlet fornisce un'implementazione api servlet quindi non è necessario aggiungere servlet-api.jar al percorso classe runtime.
Il compilatore ha bisogno del percorso di classe corretto per compilare le chiamate a una libreria (compilare le dipendenze del tempo)
La JVM necessita del percorso classe corretto per caricare le classi nella libreria che si sta chiamando (dipendenze di runtime).
Possono essere diversi in un paio di modi:
1) se la tua classe C1 chiama la classe di libreria L1 e L1 chiama la classe di libreria L2, allora C1 ha una dipendenza di runtime da L1 e L2, ma solo una dipendenza del tempo di compilazione da L1.
2) se la tua classe C1 istanzia dinamicamente un'interfaccia I1 usando Class.forName () o qualche altro meccanismo e la classe di implementazione per l'interfaccia I1 è la classe L1, allora C1 ha una dipendenza di runtime da I1 e L1, ma solo una dipendenza dal tempo di compilazione su I1.
Altre dipendenze "indirette" che sono le stesse per la fase di compilazione e di esecuzione:
3) la classe C1 estende la classe di libreria L1 e L1 implementa l'interfaccia I1 ed estende la classe di libreria L2: C1 ha una dipendenza in fase di compilazione da L1, L2 e I1.
4) la tua classe C1 ha un metodo foo(I1 i1)
e un metodo in bar(L1 l1)
cui I1 è un'interfaccia e L1 è una classe che accetta un parametro che è l'interfaccia I1: C1 ha una dipendenza in fase di compilazione da I1 e L1.
Fondamentalmente, per fare qualcosa di interessante, la tua classe deve interfacciarsi con altre classi e interfacce nel classpath. Il grafico classe / interfaccia formato da quell'insieme di interfacce di libreria produce la catena di dipendenze in fase di compilazione. Le implementazioni della libreria producono la catena delle dipendenze di runtime. Si noti che la catena delle dipendenze di runtime dipende dal runtime o è lenta: se l'implementazione di L1 a volte dipende dall'istanza di un oggetto di classe L2 e quella classe viene istanziata solo in un particolare scenario, allora non c'è dipendenza tranne quello scenario.
Java in realtà non collega nulla in fase di compilazione. Verifica solo la sintassi utilizzando le classi corrispondenti che trova in CLASSPATH. Non è fino al runtime che tutto viene messo insieme ed eseguito in base al CLASSPATH in quel momento.
Le dipendenze in fase di compilazione sono solo le dipendenze (altre classi) che usi direttamente nella classe che stai compilando. Le dipendenze di runtime coprono sia le dipendenze dirette che quelle indirette della classe in esecuzione. Pertanto, le dipendenze di runtime includono le dipendenze delle dipendenze e qualsiasi dipendenza di riflessione come i nomi di classe che hai in a String
, ma sono usati in Class#forName()
.
A
, B.jar con B extends A
e C.jar con C extends B
allora C.jar dipende dal tempo di compilazione su A.jar anche se la dipendenza di C da A è indiretta.
Per Java, la dipendenza dal tempo di compilazione è la dipendenza del codice sorgente. Ad esempio, se la classe A chiama un metodo dalla classe B, allora A dipende da B al momento della compilazione poiché A deve conoscere B (tipo di B) per essere compilato. Il trucco qui dovrebbe essere questo: il codice compilato non è ancora un codice completo ed eseguibile. Include indirizzi sostituibili (simboli, metadati) per le fonti che non sono ancora compilate o esistenti in jar esterni. Durante il collegamento, questi indirizzi devono essere sostituiti da indirizzi effettivi in memoria. Per farlo correttamente, è necessario creare simboli / indirizzi corretti. E questo può essere fatto con il tipo di classe (B). Credo che questa sia la principale dipendenza al momento della compilazione.
La dipendenza dal runtime è più correlata al flusso di controllo effettivo. Invoca indirizzi di memoria effettivi. È una dipendenza che hai quando il tuo programma è in esecuzione. Hai bisogno di dettagli di classe B qui come le implementazioni, non solo le informazioni sul tipo. Se la classe non esiste, riceverai RuntimeException e JVM uscirà.
Entrambe le dipendenze, generalmente e non dovrebbero, fluire nella stessa direzione. Tuttavia, questa è una questione di progettazione OO.
In C ++, la compilazione è leggermente diversa (non just-in-time) ma ha anche un linker. Quindi il processo potrebbe essere considerato simile a Java, immagino.