java - 这是对在 Java 中创建新对象的过程的正确描述吗?
问题描述
一般来说,我是 Java 和 OOP 的新手。在无数次尝试弄清楚创建一个新对象的过程之后,我仍然怀疑我是否正确理解了里面到底发生了什么(比如'操作符的作用是什么new
?','谁调用了构造函数?','如何构造函数是否知道要初始化什么对象?''是否this
存在于所有阶段?'等)。
假设我们有一个代码:
class NewObject {
private int varA;
private int varB;
public NewObject(int a, int b) {
varA = a;
varB = b;
}
}
public class Test {
public static void main(String args[]) {
NewObject obj = new NewObject(3, 4);
}
}
在舞台后面(我用斜体突出了那些让我最怀疑的地方):
obj
声明类的新引用变量NewObject
;- 运算符向 Java 请求一些堆内存,以使用类的声明作为蓝图
new
来分配对象。NewObject
- 运算符
new
将Java提供的内存块的地址存储在变量内部obj
new
然后,运算符在该新创建的对象中调用构造函数,并传递两个数值(显式地为 3 和 4)以及该对象的地址(this
隐式地)作为参数。- 在对象内部,构造函数创建两个局部变量
a
,b
并为它们分配从new
. 构造函数还隐式创建了一个本地this
来存储对象的地址。 - 构造函数在它的主体中看到
varA
和varB
,但没有看到显式this
附加到它们,因此它首先将它们视为局部变量。由于找不到这些局部变量的对应声明,所以它认为它们一定是实例变量。 - 构造函数因此搜索implicit
this
,当它找到时this
,它使用它的值作为一个对象的引用(地址),该对象的实例变量必须使用其局部变量的值进行初始化。
这是正确的还是我错过了什么?谢谢!
解决方案
让我们看一下您的 main 方法的反编译字节码(我省略了一些不太相关的部分):
public static void main(java.lang.String[]);
Code:
stack=4, locals=2, args_size=1
0: new #2 // class NewObject
3: dup
4: iconst_3
5: iconst_4
6: invokespecial #3 // Method NewObject."<init>":(II)V
9: astore_1
10: return
- #0 说“分配一个由 #2 引用的类型的新对象(它旁边的注释很好地告诉我们是类
NewObject
) - #3 说“复制堆栈上的最新值”(恰好是对新分配对象的引用)。现在堆栈包含对新对象的 2 个引用。
- #4 和 #5 将数字 3 和 4 放入堆栈
- #6 通过引用 #3 调用构造函数,使用
invokespecial
. 这将从堆栈中获取所需的参数并将它们从堆栈中弹出(堆栈上的最后 3 个值是对新对象的引用以及数字 3 和 4) - #9 将堆栈中的剩余值存储在局部变量 #1 中(这是对新对象的引用)
- #10 表示该
main
方法已完成并返回到调用它的任何内容。
所以new
字节码只确保对象的内存被创建,由它之后的字节码实际调用构造函数。
请注意,这可能意味着理论上您可以创建未初始化的对象并传递它们,但是 Java 运行时在它加载的字节码上运行一个称为“验证”的步骤,以验证这样的事情永远不会发生(即你可以'不仅仅是调用new
并返回值,运行时将拒绝加载试图这样做的类)。
另请注意,#9 中的步骤基本上没有意义,因为我们写入了一个从不读取的局部变量。这表明它javac
不是一个优化编译器:它非常直接地翻译 Java 源代码并且不尝试对其进行任何优化。诸如删除该存储操作之类的优化通常发生在运行时。
如果我们查看NewObject
方法的字节码,我们会看到这个(删除了一些部分):
public NewObject(int, int);
Code:
stack=2, locals=3, args_size=3
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field varA:I
9: aload_0
10: iload_2
11: putfield #3 // Field varB:I
14: return
请注意,这args_size=3
告诉我们该方法需要堆栈上的 3 个值(this
和 2 个实参)。这意味着在此级别上,this
引用被视为与任何其他参数一样。
- 在第 0 行和第 1 行中,我们加载
this
堆栈并调用超级构造函数(`Object) - 第 4 行和第 5 行加载
this
,第一个参数a
和第 6 行将设置字段 #2(这是varA
对对象引用的this
引用a
) - 第 9-11 行对 b 做同样的事情
- 第 14 行标志着构造函数的结束。
推荐阅读
- ios - 是否可以缩小金属上的深度纹理?
- maven - 如何修复原因:java.lang.NullPointerException:需要位置。JavaFX
- proteus - proteus 是否支持 onKey up/down 事件
- html - Popover 边框阴影和对齐问题
- sql - 如何成功调用包
- angular - 版本升级到 Ionic 4 时找不到模块 'ionic-angular'.ts
- c# - 比较列表
并获得包括空值在内的差异 - json - Hive:忽略创建表中的字段
- android - 在 Fragments 中保存和恢复 ListView (livedata)
- flutter - 如何在 TextFormField 中添加额外的标签?