我不能在JLS / JVMSpec中找到它,也不能在SO中找到它。我肯定一定会被问到......
那么,“新”究竟做了什么?假设我们在A:
中实例化一个B类class A {
// ...
new B();
// ...
}
这相当于
class A {
// ...
A.class.getClassLoader().loadClass("B's canonical name").newInstance();
// ...
}
是否,或者它在每个环境中都不能像那样工作?
如果您能指出JLS / JVMSpec中的相应章节,我将不胜感激。谢谢!
编辑:我们肯定无法在B.class.getCanonicalName()
来电中致电loadClass()
,因为B尚未加载。 JVM必须根据import语句解析名称。
答案 0 :(得分:15)
这相当于
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) 显示为具有特殊名称的实例初始化方法
<init>
。该名称由编译器提供。因为名称<init>
不是有效的标识符,不能直接在程序中使用 用Java编程语言编写。实例初始化 方法只能在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由bootstrap类加载器定义,那么引导程序 类加载器启动加载C(第5.3.1节)。
如果D是由用户定义的类加载器定义的,那么相同 用户定义的类加载器启动C(第5.3.2节)的加载。
否则N表示数组类。数组类由Java虚拟机(第5.3.3节)直接创建,而不是由类加载器创建。 但是,D的定义类加载器用于过程中 创建数组类C.
请注意上述引文中的句子 - If D was defined by a user-defined class loader, then that same user-defined class loader initiates loading of C
。在表达式new A()
的上下文中,加载封闭类的类加载器将负责根据VM规范加载A
;这当然是假设引导类加载器没有加载封闭类。
答案 1 :(得分:3)
跟进我的评论,像
这样的行new A()
转换为
0: new #2; //class A
3: dup
4: invokespecial #3; //Method A."<init>":()V
7: pop
堆栈跟踪是:
[1] java.net.URLClassLoader$1.run (URLClassLoader.java:202)
[2] java.security.AccessController.doPrivileged (native method)
[3] java.net.URLClassLoader.findClass (URLClassLoader.java:190)
[4] sun.misc.Launcher$ExtClassLoader.findClass (Launcher.java:229)
[5] java.lang.ClassLoader.loadClass (ClassLoader.java:307)
[6] java.lang.ClassLoader.loadClass (ClassLoader.java:296)
[7] sun.misc.Launcher$AppClassLoader.loadClass (Launcher.java:301)
[8] java.lang.ClassLoader.loadClass (ClassLoader.java:248)
[9] Loader.main (Loader.java:11)
所以我猜你的猜测非常接近。
答案 2 :(得分:-1)
我发现这个链接主要解释了Java中“new”运算符的概念。我的主要想法是这句话:
我认为应该考虑三件事:
希望它有所帮助;)