即使 class 文件丢失或有错误,预加载 class 也不会报告错误,直到首次主动使用此 class 为止?

Preloading class will not report an error until this class is first actively used even though the class file is missing or has errors?

我正在阅读下面的文章,对以下段落有疑问:

The jvm specification allows a class loader to preload a class when it expects it to be used,If encountered during preloading.class file is missing or has errors,The class loader must report a linkerror when the program first actively uses the class. If this class has not been actively used by the program,Then the class loader will not report an error.

给定以下代码:

public class MyTest1 {
    public static void main(String[] args) {
            System.out.println(MyChild1.str); 
    }
}

class MyParent1 {
    public static  String str = "hello world";
    static {
        System.out.println("MyParent1 static block");
    }
}
class MyChild1 extends MyParent1 {
    public static String str2 = "welcome";
    static {
        System.out.println("MyChild1 static block");
    }
}

运算结果为:

MyParent1 static block
hello world

MyChild1 未被主动使用但已加载。

[Loaded com.yc.test.classloader.MyParent1 from 
file:/C:/Users/nhn/IdeaProjects/demo/out/production/demo/]
[Loaded com.yc.test.classloader.MyChild1 from 
file:/C:/Users/nhn/IdeaProjects/demo/out/production/demo/]

但是当我 运行 删除 MyChild1 的 class 文件后的上述代码时,发生了 NoClassDefFoundError。

Exception in thread "main" java.lang.NoClassDefFoundError: com/yc/test/classloader/MyChild1
    at com.yc.test.classloader.MyTest1.main(MyTest1.java:5)
Caused by: java.lang.ClassNotFoundException: com.yc.test.classloader.MyChild1
    at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:418)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:355)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:351)
    ... 1 more

运算结果与文中矛盾。为什么?

https://www.tutorialfor.com/blog-228663.htm

这篇引用的文章是错误的,即使有这些未定义的术语,如“预加载”和“当程序首次主动使用 class”时也是如此。

如果“预加载”是指急切解析引用的classes,我们可以参考Java语言规范,§12.1.2.:

An implementation may resolve symbolic references from a class or interface that is being linked very early, even to the point of resolving all symbolic references from the classes and interfaces that are further referenced, recursively. (This resolution may result in errors from these further loading and linking steps.)

然后说明潜在的错误:

The only requirement on when resolution is performed is that any errors detected during resolution must be thrown at a point in the program where some action is taken by the program that might, directly or indirectly, require linkage to the class or interface involved in the error. Using the "static" example implementation choice described above, loading and linkage errors could occur before the program is executed if they involved a class or interface mentioned in the class Test or any of the further, recursively referenced, classes and interfaces.

所以说加载或链接错误仅在“程序首次主动使用 class 时”才可报告是绝对错误的,即使文章并不关心解释什么构成主动使用。

中所述, 初始化 是在明确定义的条件下执行的,并且在克服 的常见混淆时程序的行为是可以理解的加载 初始化。对表达式MyChild1.str求值,classMyChild1的加载是不可避免的,只有在class加载完成后,才有可能判断它没有field str 但 superclass 有。因此,尽管它将不可避免地 加载 ,但它不会 初始化 ,因为指定的操作(如访问 static字段)已执行。

正如您可以从堆栈跟踪中识别的那样,NoClassDefFoundError 已在计算表达式 MyChild1.str 之前抛出,这已经是尽可能惰性的了。抛出错误的有效位置应该是方法执行的开始,Mytest1 class 的加载和解析,或者,正如规范的链接部分所说,甚至在执行整个程序开始。

虽然加载和解析的点以及因此抛出错误的点留给了特定的实现,但所有有效的实现都将同意在计算表达式 MyChild1.str 之前抛出错误。