创建子类实例时是否还创建了超类实例?

时间:2014-10-05 11:28:24

标签: java inheritance

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

但是,我无法找到任何官方资料(来自Oracle)来支持这一点。我搜索了几个小时,找不到任何东西。任何人都可以向我推荐一个可靠的资源来证实这一点吗?

4 个答案:

答案 0 :(得分:2)

当您创建新实例并调用类构造函数时,堆中已保留足够的内存以存储帽子实例属性。这两个属性都可以描述:

  • 明确在您的班级定义中。
  • 在层次结构树的任何节点上。

是的,调用了超类构造函数,但只是初始化在构造函数中未明确声明的那些属性。它永远不会意味着将创建超类的新对象。

检查这些链接,它们可以帮助您了解该过程:

由于第二个链接的网站声明:创建对象的是new。即:它为所有类引用(对象属性)和原始值保留内存。 然后,调用构造函数,其目的是为属性赋值。 由于对象属性是Java中的引用,因此构造函数可以使用new来创建对象属性,它们的引用将是存储在对象内存中的值。 超类构造函数只为类继承的属性继续执行此任务。

答案 1 :(得分:1)

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

答案 2 :(得分:1)

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

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

因此,实际上,如果忽略DerivedClass实例字段,该对象看起来非常像BaseClass的实例,并且JVM可以引用该对象,就像它是BaseClass的实例一样,并且没有任何困难。

同样,DerivedClass的Class对象是一个"虚方法表"用:

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

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

但是当你仔细观察时,你会看到Object组中指向hashValue方法的指针实际指向BaseClass中的指针(如果BaseClass覆盖) hashValue)。因此,JVM可以将对象视为Object,调用hashValue,并在BaseClass(或DerivedClass中无缝地获取方法,如果它也覆盖了方法)。

(*)实际上,实例字段可以混合到一定程度,因为"对齐"来自超类的字段可能会在堆分配中留下间隙,即子类中的字段可以填充这些空白。这只是一个最小化对象大小的技巧。

答案 3 :(得分:0)

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