JAVA构造函数没有返回值是怎么赋值的?

本文详细解析了Java构造函数的特性,虽然构造函数在语法上无返回值,但在虚拟机层面,构造函数表现为一个名为`init`的实例初始化方法。通过反编译代码,我们可以看到对象的创建和赋值过程,涉及`new`、`dup`、`invokespecial`等指令。在方法调用过程中,栈帧的局部变量表和操作数栈起到了关键作用,确保了对象的正确初始化和引用的存储。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

众所周知,在java里是不能给构造函数写返回值的,如果在低版本的编译器定义一个构造器写上返回值可能会报错,高版本里面他就是一个普通的方法。
可是如果构造函数没有返回值,那么比如Test t = new Test()我们new一个对象的时候是怎么赋值的呢?

构造函数有返回值吗

写一段代码测试一下:

public class Test {
    public Test() {
       
    }

    public static void main(String[] args) {
        Test t = new Test();
    }
}

反编译一下看看:

 Code:
       0: new           #5 // class com/irving/utils/baidu/Test
       3: dup
       4: invokespecial #6 // Method "<init>":()V
       7: astore_1
       8: return

从反编译的结果看 4: invokespecial #7 // Method “init”: ()V,调用构造函数,V代表void无返回值,那么init代表什么含义?

我在书里找到这样一段话:

在 Java 虚拟机层面上,Java 语言中的构造函数是以一个名为init的特殊实例初始化方法的形式出现的,init这个方法名称是由编译器命名的,因为它并非一个合法的 Java 方法名字,不可能通过程序编码的方式实现。实例初始化方法只能在实例的初始化期间,通过 Java 虚拟机的 invokespecial 指令来调用, 只有在实例正在构造的时候,实例初始化方法才可以被调用访问。

一个类或者接口最多可以包含不超过一个类或接口的初始化方法,类或者接口就是通过这个方法完成初始化的。这个方法是一个不包含参数的静态方法,名为clinit。这个名字也是由编译器命名的,因为它并非一个合法的 Java 方法名字,不可能通过程序编码的方式实现。类或接口的初始化方法由 Java 虚拟机自身隐式调用,没有任何虚拟机字节码指令可以调用这个方法,只有在类的初始化阶段中会被虚拟机自身调用。

init代表着虚拟机调用构造函数,现在情况很明显,构造函数返回类型是void,那么它究竟是怎么赋值的呢?

赋值探究

我们明白一点,方法的调用过程就是栈帧入栈和出栈的过程,栈帧随着方法的调用创建,方法结束销毁。栈帧的内部包含局部变量表、操作数栈、动态链接等。

局部变量表表示方法调用时候的参数传递,当一个实例方法被调用的时候,第0个局部变量存储了当前实例方法所在对象的引用(this),后续的其他参数传递至1到N的连续位置。

操作数栈用来准备方法调用的参数和返回结果。

gzh_58185da0.png

以上面测试代码的方法来看Test t = new Test() 的调用过程:

  1. new 创建Test对象,并将其引用值压入操作数栈顶
  2. dup 复制栈顶数值并将复制值压入栈顶
  3. invokespecial 使用dup复制的引用并用来初始化,此时栈顶应该只有new创建的原始引用
  4. astore_1 将new创建的引用存入局部变量表索引为1的位置
  5. return 方法正常返回

gzh_c5d39ec3.png

从这个过程我们已经看出来了,整个过程最后我们最终拿到了new之后创建的对象引用,并且保存到局部变量表中,可以供我们继续使用。

关注公众号:java宝典
a

### Java 构造函数的定义 构造函数是一种特殊的成员方法,其名称与类名完全一致。它主要用于在创建对象时初始化对象的状态。构造函数没有返回值类型,甚至不使用 `void` 关键字表示无返回值[^2]。 #### 构造函数的特点 - **名称一致性**:构造函数的名字必须与其所属的类名相同。 - **无返回值声明**:与其他方法不同的是,构造函数不需要指定任何返回值类型,即使是没有返回值也不写 `void`。 - **自动调用**:当通过 `new` 运算符实例化一个对象时,系统会自动调用相应的构造函数来完成对象的初始化工作。 ### Java 构造函数的定义方式 以下是构造函数的基本语法结构: ```java 修饰符 类名(参数列表){ // 初始化代码 } ``` 例如,在下面的例子中展示了如何为名为 `Dog` 的类定义带两个参数(字符串类型的 `myName` 和整型的 `myAge`)的构造函数: ```java public class Dog { String name; int age; public Dog(String myName, int myAge) { this.name = myName; // 将传入的参数赋值给实例变量 this.age = myAge; } } ``` 上述代码片段中的构造函数接收两个参数并分别用于设置狗的名字和年龄属性。 ### 多态性——构造函数重载 如同普通的方法一样,构造函数也支持重载机制。这意味着可以在同一个类里定义多个具有不同签名(不同的参数数量或者类型顺序)的构造函数。这样可以根据实际需求灵活地提供多种途径去构建对象实例。 举个例子来说,除了上面提到的那个接受名字和年龄作为输入参数版本之外,还可以增加另一个只设定名字而不涉及年龄字段初始值的情况下的构造函数形式如下所示: ```java // 只有名字参数的构造函数 public Dog(String myName) { this(myName, 0); // 调用了前面定义好的双参版构造器实现部分共享逻辑复用效果更佳 } // 或者直接简单处理仅含单个String类型数据成员情况 public Dog(String myName) { this.name = myName; this.age = 0; // 默认设初值为零岁幼犬状态 } ``` 这里值得注意的一点是我们利用了一个叫做“this”的关键字来进行内部其他已存在变种构造器之间的互相引用操作从而达到减少重复编写相似功能代码的目的同时也增强了程序可读性和维护便利程度等方面的优势所在。 ### 创建对象时构造函数的作用过程解析 当我们执行像这样的语句:`Dog d=new Dog("Buddy",5)`的时候会发生什么呢? 1. 首先分配内存空间准备容纳即将诞生的新个体实体; 2. 接着定位到匹配当前传递过来实参特征最相符的具体哪一个特定形态样式的构造表达式主体内容上去执行相应动作序列直至结束为止整个流程才算彻底完结达成目标成果产出预期结果呈现出来供后续进一步开发利用等等一系列连贯紧密衔接有序排列组合而成的整体运作体系框架之中不可或缺的重要组成部分之一而已啦![^1] ### 总结 综上所述,Java 中的构造函数是用来初始化新创建的对象的关键工具。它们不仅能够简化对象初始化的过程,还能提高代码的清晰度和效率。通过合理运用构造函数及其特性如重载等功能可以让我们的应用程序设计更加优雅高效实用性强等特点表现得淋漓尽致呢!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值