编译期间 java 和 运行 期间类加载器的顺序

order of classloader in java during compile time and run time

我在 class 下面创建了 java 中的限定名称 java.lang.String

package java.lang;

public class String {
    public int getValue() {
        return 42;
   }
}

在 Main Class 中,我添加了以下代码。

public class Main {

    public static void main(String[] args) {
        String s = new String();
        System.out.println(s.getValue());
    }
}

代码编译正常。当我 运行 代码时,它失败并出现以下错误。

 Exception in thread "main" java.lang.NoSuchMethodError:    java.lang.String.getValue()I
at com.Main.main(Main.java:12)

我了解到 java.lang.String 是由 bootstrap classloader 在 运行 时间从 rt.jar 文件加载的。

所以,我认为class加载的顺序在编译时和运行时应该是不同的。能否请您给出 class 在编译时和 运行 时加载的顺序。

首先,此测试在 Java 9 或更高版本中不起作用。尝试编译 String class 将出现此错误:

java/lang/String.java:1: error: package exists in another module: java.base
package java.lang;
^

在 Java 8,我得到了你看到的行为。假设调整后的 String class 在同一源代码树中,Main class 可以编译,但是当您尝试 运行 它时会出现异常。

这似乎被报告为 bug 4929425。解决方案是这是一个文档错误,他们澄清了 javac 命令的文档......虽然可能还不够。

总之,还是有区别的,而且是微妙的。

java 命令仅按以下顺序搜索:

  • bootstrap class路径
  • 扩展目录
  • class路径

javac命令首先搜索源目录。如果它在那里找到源文件,它会在同一位置查找相应的 class 文件,并(如有必要)编译或重新编译它。如果未找到源文件,则它会在 class 路径中搜索 class 文件,如上文所述 java.

请注意,需要非常仔细地阅读 javac 手册条目才能弄清这一点。很容易错过。 (https://docs.oracle.com/javase/7/docs/technotes/tools/windows/javac.html#searching)

(IMO,他们可以使手册页更清晰。但是,如果您试图覆盖 bootstrap class 路径上的某些 class 或在扩展目录中。而您以错误的方式进行操作。基本上,这是一种边缘情况。清楚地记录晦涩的边缘情况的问题在于,您最终可能会使正常情况下的文档更加混乱。)