Referring to the Oracle Documentation, the statement from the SCJP book may be oversimplified. The Oracle Documentation explicitly differentiates between the "Java Launcher" (java
) and the Java Compiler javac
. And in fact, the processes are somewhat different.
I'll try to extract the relevant parts that explain the behavior that you are observing:
(From How Classes are Found : How Javac and JavaDoc Find Classes:)
If a referenced class is defined in both a class file and source file, [...] javac uses class files, but automatically recompiles any class files it determines to be out of date. The rules for automatic recompilation are documented in the javac document for Windows or Solaris.
These linked docments contain the corresponding subsection (which is the same in both cases), from which I'll quote here again:
(From javac - Java programming language compiler : SEARCHING FOR TYPES:)
When compiling a source file, the compiler often needs information about a type whose definition did not appear in the source files given on the command line. [...]
When the compiler needs type information, it looks for a source file or class file which defines the type. [...]
A successful type search may produce a class file, a source file, or both. If both are found, you can use the -Xprefer
option to instruct the compiler which to use. If newer is given, the compiler will use the newer of the two files. If source is given, it will use the source file. The default is newer.
If a type search finds a source file for a required type, either by itself, or as a result of the setting for -Xprefer
, the compiler will read the source file to get the information it needs. In addition, it will by default compile the source file as well. You can use the -implicit
option to specify the behavior. If none is given, no class files will be generated for the source file. If class is given, class files will be generated for the source file.
So to summarize: The javac
compiler will find your source file for java.util.HashSet
, as well as the class file from the bootstrap classes. But by default, it will compile the source file.
(And interestingly, there seems to be no easy way to convince him not to use the source as input: The -implicit
option only determines whether a .class
file is generated, but even if -implicit:none
is set, it will still use the class that was created from the source...)
You can also use the -verbose
option to watch this process in more detail:
javac -verbose java/util/Lol.java
produces the following 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
It does not even try to load the HashSet` class from the bootstrap JARs, but instead, directly refers to your source file:
[loading RegularFileObject[.\java\util\HashSet.java]]
In contrast, when you omit your own HashSet
class, you'll see the expected 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 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)]]
...
where it obtains the HashSet
class from the rt.jar
.