这是对在 Java 中创建新对象的过程的正确描述吗?
Posted
技术标签:
【中文标题】这是对在 Java 中创建新对象的过程的正确描述吗?【英文标题】:Is that a correct depiction of the process of creating a new object in Java? 【发布时间】:2020-05-06 11:45:34 【问题描述】:总的来说,我是 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);
幕后(我用斜体标出了最让我怀疑的地方):
-
声明了类
NewObject
的新引用变量obj
;
操作员new
使用类NewObject
的声明作为蓝图,向Java 请求一些堆内存来分配对象。
运算符new
在变量obj
中存储了Java提供的内存块的地址
操作员new
然后调用新创建的对象内的构造函数并传递两个数值(明确地为 3 和 4),以及该对象的地址 (@987654331 @ 隐含) 作为参数。
在对象内部,构造函数创建两个局部变量a
和b
,并为它们分配从new
接收的值。 构造函数还隐式创建了一个本地this
来存储对象的地址。
构造函数在其主体中看到varA
和varB
,但没有看到明确的this
附加到它们,因此它首先将它们视为局部变量。由于找不到这些局部变量的对应声明,所以认为它们一定是实例变量。
构造函数因此搜索隐式this
,当它找到this
时,它使用它的值作为一个对象的引用(地址),该对象的实例变量必须使用其局部变量中的值进行初始化。
这是正确的还是我错过了什么?谢谢!
【问题讨论】:
【参考方案1】:让我们看一下你的 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 使用invokespecial
通过引用#3 调用构造函数。这将从堆栈中获取所需的参数并将它们从堆栈中弹出(堆栈上的最后 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
引用被视为与任何其他参数一样。
this
加载到堆栈上并调用(`Object)的超级构造函数
第 4 行和第 5 行加载 this
和第一个参数 a
和第 6 行将设置字段 #2(这是对 this
对象引用的 varA
的引用到值a
)
第 9-11 行对 b 执行相同操作
第 14 行标志着构造函数的结束。
【讨论】:
非常感谢!这帮助很大!以上是关于这是对在 Java 中创建新对象的过程的正确描述吗?的主要内容,如果未能解决你的问题,请参考以下文章
Pyqt5中的QThreads:在worker类的构造函数中创建新对象可以吗?
在 SharePoint 中创建新的 Web 应用程序时,我可以在应用程序池名称中添加空格吗?