首先要了解的是对象实例化如何在字节码级别上进行。
如JVMS第3.8节所述。使用类实例:
Java虚拟机类实例是使用Java虚拟机的 新
指令创建的。回想一下,在Java虚拟机级别上,构造函数显示为带有编译器提供的name的方法<init>。这种特别命名的方法称为实例初始化方法(第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.<init>()V 7 areturn
因此,构造函数调用通过
invokespecial与共享
this作为第一个参数传递的行为
invokevirtual。
但是,必须强调的是,对未初始化引用的引用要特别对待,因为在调用构造函数(或位于构造函数内部的超级构造函数)之前,不允许您使用它。这是由验证者强制执行的。
JVMS,第4.10.2.4节。实例初始化方法和新创建的对象:
…类的实例初始化方法(第2.9节)
myClass将新的未初始化对象视为其this在局部变量0中的参数。在该方法调用myClass或其直接父类的另一个实例初始化方法之前,this该方法可以执行的唯一操作this是分配字段在内声明myClass。在对实例方法进行数据流分析时,验证程序将局部变量0初始化为包含当前类的对象,或者,例如,对于初始化方法,局部变量0包含指示未初始化对象的特殊类型。在此对象上调用了适当的实例初始化方法(从当前类或其直接超类)后,在操作数堆栈的验证程序模型和局部变量数组中出现的所有此特殊类型都将替换为当前类类型。验证者拒绝在初始化之前使用新对象或多次初始化对象的代码。另外,它确保方法的每个常规返回都在该方法的类或直接超类中调用了实例初始化方法。
同样,将创建一个特殊类型,并将其作为Java虚拟机指令 new
的结果推送到操作数堆栈的验证程序模型上。特殊类型指示创建类实例所依据的指令以及未初始化的类实例所创建的类型。当在未初始化的类实例的类中声明的实例初始化方法在该类实例上调用时,所有特殊类型的事件都将替换为该类实例的预期类型。随着数据流分析的进行,这种类型的变化可能会传播到后续指令。
因此,通过 新
指令创建对象的代码无法在调用构造函数之前以任何方式使用它,而构造函数的代码只能在调用另一个(
this(…)或
super(…))构造函数之前分配字段(内部类用来初始化的机会)他们的外部实例引用作为第一个动作),但对于未初始化的对象仍然无法执行其他任何操作。
当构造
this器仍处于未初始化状态时,也不允许其返回。因此,自动生成的构造函数具有所需的最小值,调用超级构造函数并返回(在字节码级别上没有隐式返回)。
通常建议阅读Java虚拟机规范(及其Java
11版本)以及任何ASM特定文档或教程。



