当前位置: 首页 > 面试题库 >

构造函数字节码

邰昀
2023-03-14
问题内容

该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已知的值如何?


问题答案:

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

如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特定文档或html" target="_blank">教程。



 类似资料:
  • JavaScript 中的构造函数和其它语言中的构造函数是不同的。 通过 new 关键字方式调用的函数都被认为是构造函数。 在构造函数内部 - 也就是被调用的函数内 - this 指向新创建的对象 Object。 这个新创建的对象的 prototype 被指向到构造函数的 prototype。 如果被调用的函数没有显式的 return 表达式,则隐式的会返回 this 对象 - 也就是新创建的对象

  • 我正在尝试用Byte Buddy拦截构造函数调用,这是我的示例代码: 我看到了这个相关的问题,但是,我得到了一个冲突异常(就好像构造函数被定义了两次)。

  • 主要内容:默认拷贝构造函数拷贝和复制是一个意思,对应的英文单词都是 。 对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的图片拷贝到桌面以方便浏览,将重要的文件上传到百度网盘以防止丢失等,都是「创建一份新数据」的意思。 在 C++ 中,拷贝并没有脱离它本来的含义,只是将这个含义进行了“特化”,是指用已经存在的对

  • 主要内容:1、实例构造函数,2、静态构造函数,3、私有构造函数在 C# 中,构造函数就是与类(或结构体)具有相同名称的成员函数,它在类中的地位比较特殊,不需要我们主动调用,当创建一个类的对象时会自动调用类中的构造函数。在程序开发的过程中,我们通常使用类中的构造函数来初始化类中的成员属性。 C# 中的构造函数有三种: 实例构造函数; 静态构造函数; 私有构造函数。 1、实例构造函数 构造函数是类中特殊的成员函数,它的名称与它所在类的名称相同,并且没有返回值。当

  • 主要内容:构造函数的重载,默认构造函数在 C++中,有一种特殊的成员函数,它的名字和类名相同,没有返回值,不需要用户显式调用(用户也不能调用),而是在创建对象时自动执行。这种特殊的成员函数就是 构造函数(Constructor)。 在《 C++类成员的访问权限以及类的封装》一节中,我们通过成员函数 setname()、setage()、setscore() 分别为成员变量 name、age、score 赋值,这样做虽然有效,但显得有点

  • 本文向大家介绍Java构造函数,包括了Java构造函数的使用技巧和注意事项,需要的朋友参考一下 构造函数与方法相似,但在以下方面有所不同。 它们没有任何返回类型。 构造函数的名称与类的名称相同。 每个类都有一个构造函数。如果我们未为类明确编写构造函数,则Java编译器将为该类建立默认构造函数。 每次创建一个新对象时,将至少调用一个构造函数。 一个类可以具有多个构造函数。 示例 输出结果