有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java在创建子类实例时是否也创建了超类实例?

我已经看到许多线程(例如:Inheritance in Java - creating an object of the subclass invokes also the constructor of the superclass. Why exactly?)说在创建子类实例时不会创建超类实例。我实际上同意这个观点

但是,我找不到任何官方材料(来自Oracle)来支持这一点。我找了几个小时,什么也没找到。有人能告诉我一个可靠的资源来证实这一点吗


共 (4) 个答案

  1. # 1 楼答案

    当派生类的对象被实例化时,基类的对象不会被实例化。继承只将基类的某些属性和方法带到派生类。当生成/销毁派生类的对象时,基类的构造函数/析构函数将与派生类的构造函数/析构函数一起调用。但这并不意味着基类的对象也被生成

  2. # 2 楼答案

    对象由其地址标识,地址存储在对象类型的变量中。new运算符返回该地址,并且只返回一个地址,因此只能有一个对象。例如,您可以通过查看子类和超类构造函数中的System.identityHashCode(this)来检查这一点

  3. # 3 楼答案

    创建派生类的实例时,堆分配类似(*):

    • 标准JVM对象头(带有指向DerivedClass类对象的指针)
    • Object的实例字段
    • BaseClass的实例字段
    • DerivedClass的实例字段

    因此,实际上,如果忽略DerivedClass实例字段,对象看起来非常像基类的实例,JVM可以像引用基类的实例一样引用对象,并且这样做没有困难

    类似地,在DerivedClass的类对象中有一个“虚拟方法表”,它具有:

    • Object的虚拟方法指针
    • BaseClass的虚拟方法指针
    • DerivedClass的虚拟方法指针

    JVM通过索引此表来进行虚拟调用,以查找特定的方法,例如,hashValue是方法编号5,printTheGroceryList是方法编号23。调用方法所需的编号是在调用类的方法引用数据中加载和缓存类时确定的,因此调用方法是:获取编号,转到实例头指向的类对象,索引到虚拟方法表,拉出指针,然后分支到方法

    但是当你仔细观察时,你会发现,例如,Object组中指向hashValue方法的指针实际上指向BaseClass中的指针(如果基类重写hashValue)。因此JVM可以将对象视为Object,调用hashValue,并在BaseClass中无缝地获取方法(或者DerivedClass,如果它也重写了该方法)

    (*)在实践中,实例字段可能会在一定程度上混杂在一起,因为来自超类的“对齐”字段可能会在堆分配中留下空白,而来自子类的字段可以填补空白。这只是一个最小化对象大小的技巧

  4. # 4 楼答案

    创建新实例并调用类构造函数时,堆中会保留足够的内存,以便存储该实例的属性。这些属性包括:

    • 直接属于类定义的属性
    • 属于类层次结构树中所有上层节点的属性

    是的,调用了超类构造函数,但其唯一目的是初始化超类的属性。它绝不意味着将创建超类的新对象

    查看这些链接,它们可能会帮助您了解流程:

    在第二个链接上,文档声明:创建对象的是new。也就是说:它为所有类引用(对象属性)和基元值保留内存。 然后,调用构造函数,其目的是初始化封闭类的属性。 由于对象属性是Java中的引用,构造函数可以使用new创建对象属性,它们的引用将是存储在对象内存中的值。 超类构造函数只是为类继承的属性继续此任务