l'ordine classpath javac contraddice la documentazione Oracle?


In questo modo si ottiene un buon risultato.]}

"java e javac [...] per prima cosa guarda nelle directory che contengono le classi fornite di serie con Java SE. Quindi guardano nelle directory definite da classpaths "

La documentazione Oracle indica lo stesso ordine.

(So che non dovrei farlo, ma...) Per testare questo comportamento ho implementato HashSet.java e Lol.java in directory: C:\dev\cmdline\TestProject\sources\java\util

package java.util;
public class HashSet {}

E

package java.util;
import java.util.HashSet;
public class Lol {
    public static void main(String... x) {
        HashSet a = new HashSet();
        a.add("lol");
    }
}

Ricevo un errore di compilazione durante l'esecuzione: C:\dev\cmdline\TestProject\sources > javac java / util / Lol.java

Java\util\Lol.java: 6: errore: impossibile trovare il simbolo a. aggiungi ("lol"); ^ simbolo: metodo add (Stringa) posizione: variabile a di tipo HashSet

...il che significa che il classpath predefinito (directory corrente) è il primo utilizzare.

Quindi, la documentazione Oracle è sbagliata? Come testare l'ordine classpaths?

Author: Umberto Raimondi, 2015-03-15

2 answers

Facendo riferimento alla documentazione Oracle, la dichiarazione del libro SCJP potrebbe essere semplificata eccessivamente. La documentazione Oracle distingue esplicitamente tra" Java Launcher " (java) e il compilatore Java javac. E infatti, i processi sono in qualche modo diversi.

Cercherò di estrarre le parti rilevanti che spiegano il comportamento che stai osservando:

(Da Come si trovano le classi: come Javac e JavaDoc trovano le classi:)

Se una classe di riferimento è definita sia in un file di classe che in un file sorgente, [...] javac utilizza i file di classe, ma ricompila automaticamente tutti i file di classe che determina non aggiornati. Le regole per la ricompilazione automatica sono documentate nel documento javac per Windows o Solaris .

Questi documenti collegati contengono la sottosezione corrispondente (che è la stessa in entrambi i casi), da cui citerò di nuovo qui:

(Da javac - Compilatore di linguaggi di programmazione Java: RICERCA DI TIPI:)

Quando si compila un file sorgente, il compilatore ha spesso bisogno di informazioni su un tipo la cui definizione non è stata visualizzata nei file di origine forniti nella riga di comando. [...]

Quando il compilatore ha bisogno di informazioni sul tipo, cerca un file sorgente o un file di classe che definisca il tipo. [...]

Una ricerca di tipo riuscita può produrre un file di classe, un file sorgente o entrambi. Se entrambi si trovano, è possibile utilizzare l'opzione -Xprefer per istruire il compilatore quale utilizzare. Se viene fornito più recente , il compilatore utilizzerà il più recente dei due file. Se viene fornito source , verrà utilizzato il file sorgente. Il valore predefinito è più recente .

Se una ricerca di tipo trova un file di origine per un tipo richiesto, da solo o come risultato dell'impostazione per -Xprefer, il compilatore leggerà il file di origine per ottenere le informazioni di cui ha bisogno. Inoltre, sarà per impostazione predefinita compila anche il file sorgente. È possibile utilizzare l'opzione -implicit per specificare il comportamento. Se viene specificato none , non verranno generati file di classe per il file sorgente. Se viene fornita class, i file di classe verranno generati per il file sorgente.

Quindi per riassumere: Il compilatore javactroverà il file source per java.util.HashSet, così come il file class dalle classi bootstrap. Ma per impostazione predefinita, compilerà il file source .

(Ed è interessante notare che non sembra esserci un modo semplice per convincerlo a non usare la sorgente come input: l'opzione -implicit determina solo se un file .class è generato , ma anche se -implicit:none è impostato, continuerà a usare la classe che è stata creata dalla sorgente...)

È anche possibile utilizzare l'opzione -verbose per guardare questo processo in modo più dettagliato:

javac -verbose java/util/Lol.java

Produce il seguente output:

[parsing started RegularFileObject[java\util\Lol.java]]
[parsing completed 100ms]
[search path for source files: .]
[search path for class files: (A long list with rt.jar and related JARs)]
[loading RegularFileObject[.\java\util\HashSet.java]]
[parsing started RegularFileObject[.\java\util\HashSet.java]]
[parsing completed 0ms]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/String.class)]]
[checking java.util.Lol]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/AutoCloseable.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Byte.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Character.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Short.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Long.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Float.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Integer.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Double.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Boolean.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Void.class)]]
java\util\Lol.java:6: error: cannot find symbol
        a.add("lol");
         ^
  symbol:   method add(String)
  location: variable a of type HashSet
[checking java.util.HashSet]
[total 1072ms]
1 error

Non ha nemmeno provare a caricare il La classe HashSet ' dai JAR di bootstrap, ma invece, si riferisce direttamente al tuo file sorgente:

[loading RegularFileObject[.\java\util\HashSet.java]]

Al contrario, quando ometti la tua classe HashSet, vedrai l'output previsto:

[parsing started RegularFileObject[java\util\Lol.java]]
[parsing completed 100ms]
[search path for source files: .]
[search path for class files: (A long list with rt.jar and related JARs) ]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/util/HashSet.class)]]
[loading ZipFileIndexFileObject[c:\jdk1.8.0\lib\ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]]
...

Dove ottiene la classe HashSet dal rt.jar.

 2
Author: Marco13, 2015-03-15 12:12:30

La JVM e il compilatore Java sono due cose molto diverse, mentre il comportamento descritto da SCJP è quello previsto da una JVM, non sono così sicuro che javac segua le stesse regole, nella nota tecnica Sun che hai collegato non è chiaramente indicato che i due seguano lo stesso approccio.

L'unica riga comprensibile afferma che:

Per impostazione predefinita, javac e javadoc cercano nel percorso della classe utente sia i file di classe che i file di codice sorgente.

Ma non specifica qualsiasi ordine per quanto riguarda la ricerca di classe in altri percorsi.

Sono più espliciti qui:

JDK, JVM e altri strumenti JDK trovano le classi ricercando le classi Java Platform (bootstrap), tutte le classi di estensione e il percorso della classe, in questo ordine. (Per i dettagli sulla strategia di ricerca, vedere Come vengono trovate le classi.)

L'unico documento che conta in caso di dubbio è la specifica JVM, ma che non contiene dettagli sul utensili java.

E come nota a margine, si noti che non sarà possibile richiamare le classi definite dall'utente create in java.pacchetto util, perché questo e altri pacchetti standard (cioè java.* ) sono pacchetti limitati, si otterrà un'eccezione quando si tenta di richiamare una classe utente definita in quei pacchetti (javac compilerà comunque il codice, non viene eseguito alcun controllo, ma la jvm si lamenterà).

Modifica:

Compila il tuo esempio aggiungendo il-verbose opzione:

javac -verbose java/util/Lol.java

[parsing started RegularFileObject[java/util/Lol.java]]
[parsing completed 37ms]
***[search path for source files: .]
[search path for class files: /Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/resources.jar,/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/jre/lib/rt.jar,[...],.]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Object.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/String.class)]]
***[checking java.util.Lol]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/io/Serializable.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/AutoCloseable.class)]]
***[loading RegularFileObject[./java/util/HashSet.java]]
***[parsing started RegularFileObject[./java/util/HashSet.java]]
[parsing completed 0ms]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Byte.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Character.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Short.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Long.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Float.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Integer.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Double.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Boolean.class)]]
[loading ZipFileIndexFileObject[/Library/Java/JavaVirtualMachines/jdk1.8.0_25.jdk/Contents/Home/lib/ct.sym(META-INF/sym/rt.jar/java/lang/Void.class)]]
java/util/Lol.java:8: error: cannot find symbol
        a.add("lol");
         ^
symbol:   method add(String)
location: variable a of type HashSet
[checking java.util.HashSet]
[total 449ms]
1 error

Guarda le linee con *** , come puoi vedere, javac mantiene due diversi elenchi di percorsi, uno per i sorgenti e uno per i file di classe. Mentre il comportamento per i file di classe è quello descritto nei documenti, javac controlla prima se un .il file sorgente java per un oggetto specifico esiste e se qualsiasi javac compila / carica quella classe definita dall'utente. In modo che la linea sia effettivamente corretta per quanto riguarda il caricamento della classe, semplicemente non specificano quale sia l'ordine globale (fonti+classi dal classpath) è durante la compilazione. Le fonti vengono sempre prima.

 1
Author: Umberto Raimondi, 2015-03-15 12:09:52