这是对在 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 @ 隐含) 作为参数。 在对象内部,构造函数创建两个局部变量ab,并为它们分配从new 接收的值。 构造函数还隐式创建了一个本地this 来存储对象的地址。 构造函数在其主体中看到varAvarB,但没有看到明确的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 引用被视为与任何其他参数一样。

在第 0 行和第 1 行中,我们将 this 加载到堆栈上并调用(`Object)的超级构造函数 第 4 行和第 5 行加载 this 和第一个参数 a 和第 6 行将设置字段 #2(这是对 this 对象引用的 varA 的引用到值a) 第 9-11 行对 b 执行相同操作 第 14 行标志着构造函数的结束。

【讨论】:

非常感谢!这帮助很大!

以上是关于这是对在 Java 中创建新对象的过程的正确描述吗?的主要内容,如果未能解决你的问题,请参考以下文章

Pyqt5中的QThreads:在worker类的构造函数中创建新对象可以吗?

如何在现有 WCF 服务中创建新方法?

在 SharePoint 中创建新的 Web 应用程序时,我可以在应用程序池名称中添加空格吗?

在JNI中创建新对象

将对象存储到数组中,而不在 AWS AppSync 架构中创建新表

Java:在 Ruby on Rails 应用程序中创建新“产品”的 HTTP Post