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