在Sierra/Bates SCJP书第 797 页中:
“java 和 javac [...] 首先查找包含 Java SE 标准类的目录。然后它们查找由类路径定义的目录”
Oracle 文档说明了相同的顺序。
(我知道我不应该这样做,但是......)为了测试这种行为,我在目录中实现了HashSet.java和Lol.java:C:\dev\cmdline\TestProject\sources\java\util
package java.util;
public class HashSet {}
Run Code Online (Sandbox Code Playgroud)
和
package java.util;
import java.util.HashSet;
public class Lol {
public static void main(String... x) {
HashSet a = new HashSet();
a.add("lol");
}
}
Run Code Online (Sandbox Code Playgroud)
执行时出现编译错误:C:\dev\cmdline\TestProject\sources>javac java/util/Lol.java
java\util\Lol.java:6: 错误: 找不到符号 a.add("lol"); ^符号:方法add(String)位置:HashSet类型的变量a
...这意味着首先使用默认的类路径(当前目录)。
那么,Oracle文档有错吗?您将如何测试类路径顺序?
参考 Oracle 文档,SCJP 书中的陈述可能过于简单化。Oracle 文档明确区分了“Java 启动器”( java) 和 Java 编译器javac。事实上,过程有些不同。
我将尝试提取解释您所观察到的行为的相关部分:
(来自如何找到类:Javac 和 JavaDoc 如何查找类:)
如果在类文件和源文件中都定义了引用的类,[...] javac 将使用类文件,但会自动重新编译它确定为过时的任何类文件。自动重新编译的规则记录在Windows或Solaris的 javac 文档中。
这些链接的文档包含相应的小节(在两种情况下都是相同的),我将再次在此引用:
(来自javac - Java 编程语言编译器:搜索类型:)
编译源文件时,编译器通常需要有关其定义未出现在命令行给出的源文件中的类型的信息。[...]
当编译器需要类型信息时,它会查找定义该类型的源文件或类文件。[...]
成功的类型搜索可能会生成类文件、源文件或两者。如果两者都找到,您可以使用该
-Xprefer选项来指示编译器使用哪一个。如果给出较新的文件,编译器将使用两个文件中较新的一个。如果给出了源,它将使用源文件。默认值较新。如果类型搜索找到所需类型的源文件(无论是其本身还是由于设置的结果)
-Xprefer,编译器将读取源文件以获取所需的信息。此外,它默认也会编译源文件。您可以使用该-implicit选项来指定行为。如果没有给出,则不会为源文件生成任何类文件。如果给出了类,将为源文件生成类文件。
总结一下:javac编译器将找到 的源文件java.util.HashSet以及引导类中的类文件。但默认情况下,它会编译源文件。
(有趣的是,似乎没有简单的方法可以说服他不要使用源作为输入:该-implicit选项仅确定是否生成.class文件,但即使设置了,它仍然会使用从源创建的类...)-implicit:none
您还可以使用该-verbose选项更详细地观看此过程:
javac -verbose java/util/Lol.java
Run Code Online (Sandbox Code Playgroud)
产生以下输出:
[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
Run Code Online (Sandbox Code Playgroud)
它甚至不会尝试从引导 JAR 加载 HashSet` 类,而是直接引用您的源文件:
[loading RegularFileObject[.\java\util\HashSet.java]]
Run Code Online (Sandbox Code Playgroud)
相反,当您省略自己的HashSet类时,您将看到预期的输出:
[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)]]
...
Run Code Online (Sandbox Code Playgroud)
HashSet它从哪里获取类rt.jar。
| 归档时间: |
|
| 查看次数: |
682 次 |
| 最近记录: |