javac classpath命令与Oracle文档相矛盾?

时间:2015-03-15 10:22:49

标签: java jvm

Sierra/Bates SCJP第797页:

  

“java和javac [...]首先查看包含Java SE标准类的目录。   然后他们查看类路径“

定义的目录

Oracle documentation正在说明相同的顺序。

(我知道我不应该这样做但是......)为了测试这种行为,我在目录中实现了 HashSet.java Lol.java : C:\ dev的\ CMDLINE \ TestProject \源\的java \ util的

package java.util;
public class HashSet {}

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

执行时出现编译错误: C:\ dev \ cmdline \ TestProject \ sources> javac java / util / Lol.java

  

java \ util \ lol.java:6:错误:找不到符号                   a.add( “笑”);                    ^     符号:方法add(String)     location:HashSet类型的变量a

...这意味着首先使用默认的类路径(当前目录)。

那么,Oracle文档是错误的吗?您将如何测试类路径顺序?

2 个答案:

答案 0 :(得分:2)

参考Oracle文档,SCJP书中的陈述可能过于简单。 Oracle文档明确区分了" Java Launcher" (java)和Java编译器javac。事实上,这些过程有些不同。

我会尝试提取解释您所观察行为的相关部分:

  

(来自How Classes are Found : How Javac and JavaDoc Find Classes :)

     

如果在类文件和源文件中都定义了引用的类,则javac使用类文件,但会自动重新编译它确定为过期的任何类文件。自动重新编译的规则记录在WindowsSolaris的javac文档中。

这些链接的文件包含相应的小节(在两种情况下都是相同的),我将在此再次引用:

  

(来自javac - Java programming language compiler : SEARCHING FOR TYPES :)

     

编译源文件时,编译器通常需要有关其定义未出现在命令行上给出的源文件中的类型的信息。 [...]

     

当编译器需要类型信息时,它会查找定义类型的源文件或类文件。 [...]

     

成功的类型搜索可能会生成类文件,源文件或两者。如果找到了两者,则可以使用-Xprefer选项指示编译器使用哪个。如果给出了较新的,编译器将使用这两个文件中较新的文件。如果给出了 source ,它将使用源文件。默认值为较新

     

如果类型搜索找到所需类型的源文件(或者单独找到),或者作为-Xprefer的设置的结果,编译器将读取源文件以获取所需的信息。此外,它默认也会编译源文件。您可以使用-implicit选项指定行为。如果给出 none ,则不会为源文件生成类文件。如果给出 class ,则将为源文件生成类文件。

总结一下:javac编译器会找到java.util.HashSet文件,以及引导类中的文件。但默认情况下,它会编译文件。

(有趣的是,似乎没有简单的方法可以说服他不要将源用作输入:-implicit选项仅确定{em}生成的.class文件是否为 >,但即使设置了-implicit:none,它仍然使用从源创建的类...)

您还可以使用-verbose选项更详细地观看此过程:

javac -verbose java/util/Lol.java

产生以下输出:

[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

它甚至尝试从引导程序JAR加载HashSet`类,而是直接引用您的源文件:

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

相反,当您省略自己的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)]]
...

HashSet获取rt.jar类。

答案 1 :(得分:1)

JVM和Java编译器是两个非常不同的东西,而SCJP描述的行为是JVM预期的行为,我不太确定javac是否遵循相同的规则,在您链接的Sun技术说明中没有明确说明两者采用相同的方法。

唯一可以理解的一句话是:

  

默认情况下,javac和javadoc搜索类文件和源代码文件的用户类路径。

但它没有指定任何关于其他路径中的类搜索的排序。

他们更明确here

  

JDK,JVM和其他JDK工具通过按顺序搜索Java平台(引导程序)类,任何扩展类和类路径来查找类。 (有关搜索策略的详细信息,请参阅如何找到类。)

唯一有疑问的文档是JVM Specification,但不包含有关java工具的详细信息。

作为旁注,请注意,您将无法调用在java.util包中创建的用户定义类,因为该和其他标准包(即java。*)是受限制的包,您将得到一个尝试调用这些包中定义的用户类时的异常(javac无论如何都会编译你的代码,不执行检查,但是jvm会抱怨)。

修改

编译添加-verbose选项的示例:

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

使用***查看行,如您所见,javac保留两个不同的路径列表,一个用于源,一个用于类文件。虽然类文件的行为是文档中描述的行为,但javac首先检查特定对象的.java源文件是否存在,以及是否有任何javac编译/加载该用户定义的类。因此,对于类加载,该行实际上是正确的,它们只是没有指定编译期间全局顺序(类路径中的源+类)是什么。来源始终是第一位的。