这是否等同于
class A {
// ...
A.class.getClassLoader().loadClass("B's canonical name").newInstance();
// ...
}
?
不,并非总是如此。
对于给定的命名空间,类装入仅执行一次,除非先前已卸载有问题的命名空间。因此,在大多数情况下,等效表达式将仅执行一次。换句话说,如果您有两个表达式 - ,则只会执行一次。Class
A.class.getClassLoader().loadClass("B's canonical name")
new A()
loadClass
构造函数的调用被 JVM 视为方法调用,但这需要 Java 编译器的合作。JVM 和编译器必须遵守 Java 虚拟机规范的第 3.9 节,其中规定:
3.9 特别命名的初始化方法
在 Java 虚拟机级别,每个构造函数 (§2.12) 都显示为具有特殊名称的实例初始化方法。此名称由编译器提供。由于该名称不是有效的标识符,因此不能直接在用 Java 编程语言编写的程序中使用它。实例初始化方法只能在 Java 虚拟机中由调用专用指令调用,并且只能在未初始化的类实例上调用。实例初始化方法采用派生该方法的构造函数的访问权限 (§2.7.4)。<init>
<init>
类或接口最多有一个类或接口初始化方法,并通过调用该方法进行初始化 (§2.17.4)。类或接口的初始化方法是静态的,不带任何参数。它具有 特殊名称 。此名称由编译器提供。由于该名称不是有效的标识符,因此不能直接在用 Java 编程语言编写的程序中使用它。类和接口初始化方法由 Java 虚拟机隐式调用。它们从不直接从任何 Java 虚拟机指令调用,而只是作为类初始化过程的一部分间接调用。<clinit>
<clinit>
本节假定与相关类相关的对象对当前线程可用。一旦对象可用,将调用与具有正确参数集的构造函数相对应的方法。Class
Class
<init>
如果尚未装入,则将使用哪个类装入器来装入类的问题有点不同,并且与new关键字无关。这取决于一个类如何引用另一个类,即符号引用是否需要在运行时常量池中解析?此上下文中的行为在 Java 虚拟机规范的第 5.3 节中定义:
5.3 创建和加载
创建以名称 N 表示的类或接口 C 包括在 Java 虚拟机的方法区域 (§3.5.4) 中构造特定于实现的 C 内部表示形式。类或接口的创建由另一个类或接口 D 触发,该类或接口 D 通过其运行时常量池引用 C。
...
Java 虚拟机使用以下三个过程之一来创建以 N 表示的类或接口 C:
请注意上面的引文中的句子。在表达式的上下文中,加载封闭类的类装入器将负责根据VM规范进行装入;当然,这是假设封闭类不是由引导类装入器装入的。If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C
new A()
A