4

ASM 指南讨论了构造函数:

package pkg;
public class Bean {
  private int f;
  public int getF() {
      return this.f;
  }
  public void setF(int f) {
      this.f = f;
  }
}

Bean 类还有一个由编译器生成的默认公共构造函数,因为程序员没有定义显式构造函数。此默认公共构造函数生成为 Bean() { super(); }. 此构造函数的字节码如下:

ALOAD 0
INVOKESPECIAL java/lang/Object <init> ()V
RETURN

第一条指令压this入操作数堆栈。第二条指令从堆栈中弹出这个值,并调用类中<init> 定义的方法Object。这对应于super() 调用,即对超类的构造函数的调用Object。您可以在这里看到,构造函数在编译类和源类中的命名不同:在编译类中它们总是命名为<init>,而在源类中它们具有定义它们的类的名称。最后最后一条指令返回给调用者。

this在构造函数的第一条指令之前,JVM 已经知道的值如何?

4

2 回答 2

5

在 JVM 级别,首先分配对象,未初始化,然后在该对象上调用构造函数。构造函数或多或少是在未初始化对象上执行的实例方法。

即使在 Java 语言中,this也存在并在构造函数的第一行包含其所有字段。

于 2018-11-09T00:02:35.590 回答
4

首先要了解的是对象实例化如何在字节码级别工作。

JVMS,§3.8 中所述。使用类实例

Java 虚拟机类实例是使用 Java 虚拟机的指令创建的。回想一下,在 Java 虚拟机级别,构造函数显示为具有编译器提供的名称的方法<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

因此,构造函数调用 via共享作为第一个参数invokespecial传递的行为。thisinvokevirtual

但是,必须强调的是,对未初始化引用的引用是特殊处理的,因为在调用构造函数(或在构造函数内部时的超级构造函数)之前不允许使用它。这是由验证者强制执行的。

JVMS,§4.10.2.4。实例初始化方法和新创建的对象

...类的实例初始化方法(第 2.9 节myClass将新的未初始化对象视为其this在局部变量 0 中的参数。在该方法调用另一个实例初始化方法myClass或其直接超类 on之前this,该方法可以执行的唯一操作this是分配字段内声明myClass

在对实例方法进行数据流分析时,验证器将局部变量 0 初始化为包含当前类的对象,或者,对于实例初始化方法,局部变量 0 包含表示未初始化对象的特殊类型。在此对象上(从当前类或其直接超类)调用适当的实例初始化方法后,此特殊类型在验证器的操作数堆栈模型和局部变量数组中的所有出现都将替换为当前类类型。验证器拒绝在新对象初始化之前使用它或多次初始化该对象的代码。此外,它确保方法的每个正常返回都调用了该方法的类或直接超类中的实例初始化方法。

类似地,作为 Java 虚拟机指令new的结果,创建一个特殊类型并将其推送到操作数堆栈的验证者模型上。特殊类型表示创建类实例的指令和创建的未初始化类实例的类型。当在该类实例上调用在未初始化的类实例的类中声明的实例初始化方法时,所有出现的特殊类型都将替换为该类实例的预期类型。随着数据流分析的进行,这种类型的变化可能会传播到后续指令。

因此,通过new指令创建对象的代码在调用构造函数之前不能以任何方式使用它,而构造函数的代码只能在调用另一个(this(…)super(…))构造函数之前分配字段(内部类用来初始化的机会他们的外部实例引用作为第一个动作),但仍然不能用他们未初始化的 this 做任何其他事情。

this当仍处于未初始化状态时,也不允许构造函数返回。因此,自动生成的构造函数具有所需的最小值,调用超级构造函数并返回(在字节码级别没有隐式返回)。

通常建议您阅读Java® 虚拟机规范(即其Java 11 版本)以及任何 ASM 特定文档或教程。

于 2018-11-09T09:51:56.190 回答