作者:金健winner | 来源:互联网 | 2024-10-14 12:25
我一直不明白为什么我们能够从其他类调用一个类的构造函数。构造函数是一种方法,通常在尝试从类中调用方法时,我们必须将该方法设为静态,以便我们可以访问它MyClass
我一直不明白为什么我们能够从其他类调用一个类的构造函数。构造函数是一种方法,通常在尝试从类中调用方法时,我们必须将该方法设为静态,以便我们可以访问它
MyClass.method();
或者我们会创建该类的一个实例,然后调用该方法(现在显然这个方法会与这个问题的前提相矛盾)
MyClass myClass = new MyClass();
myClass.method();
但是在构造函数的情况下,我们两者都不做。Java 如何在不执行这些方法的情况下调用类的构造函数?我知道类的构造函数必须对您从中调用它的类可见,即如果您正在调用的类构造函数位于不同的包中,则必须导入该包。
那么,Java 如何处理调用构造函数,以便能够在不使用上述方法的情况下调用它们呢?
回答
类构造函数在 Java 语言和 Java 字节码中具有超出普通方法的特殊支持。特别是,它们获得特殊名称
并使用不同的字节码指令调用(与正常方法调用看起来像的典型方法调用与运行时调度invokespecial
相反invokevirtual
)。
引用JVMS(JVM 规范)第 3.8 节:
Java 虚拟机类实例是使用 Java 虚拟机的新指令创建的。回想一下,在 Java 虚拟机级别,构造函数显示为具有编译器提供的名称 的方法。这种特殊命名的方法称为实例初始化方法(第 2.9 节)。一个给定的类可能存在多个实例初始化方法,对应多个构造函数。一旦创建了类实例并且其实例变量(包括类及其所有超类的实例变量)已初始化为其默认值,就会调用新类实例的实例初始化方法。例如:
Object create() {
return new Object();
}
编译为:
Method java.lang.Object create()
0 new #1 // Class java.lang.Object
3 dup
4 invokespecial #4 // Method java.lang.Object.()V
7 areturn
如您所见,用于生成此示例的参考编译器首先创建所讨论对象的未初始化实例,将其与任何构造函数参数(未显示)一起压入堆栈,并用于invokespecial
调用适当的构造函数以进行初始化物体。在语言级别,Java 不会让您编写在调用构造函数之前公开完全未初始化的对象的代码,尽管您仍然可以泄漏部分构造的对象。
值得注意的是,有不安全的方法(字面意思是在一个名为Unsafe的隐藏类中)获取对在低级别初始化但从未调用其构造函数的对象的引用。这些在某种程度上违反了 Java 语言规范,只有在绝对需要时才应格外小心地使用。