我见过很多线程(例如:Inheritance in Java - creating an object of the subclass invokes also the constructor of the superclass. Why exactly?)说在创建子类实例时不会创建超类的实例。我实际上同意这个意见。
但是,我无法找到任何官方资料(来自Oracle)来支持这一点。我搜索了几个小时,找不到任何东西。任何人都可以向我推荐一个可靠的资源来证实这一点吗?
答案 0 :(得分:2)
当您创建新实例并调用类构造函数时,堆中已保留足够的内存以存储帽子实例属性。这两个属性都可以描述:
是的,调用了超类构造函数,但只是初始化在构造函数中未明确声明的那些属性。它永远不会意味着将创建超类的新对象。
检查这些链接,它们可以帮助您了解该过程:
由于第二个链接的网站声明:创建对象的是new
。即:它为所有类引用(对象属性)和原始值保留内存。
然后,调用构造函数,其目的是为属性赋值。
由于对象属性是Java中的引用,因此构造函数可以使用new
来创建对象属性,它们的引用将是存储在对象内存中的值。
超类构造函数只为类继承的属性继续执行此任务。
答案 1 :(得分:1)
对象由其地址标识,存储在对象类型的变量中。 new
运算符返回该地址,只返回一个地址,因此只能有一个对象。例如,您可以通过查看子类和超类构造函数中的System.identityHashCode(this)
来检查这一点。
答案 2 :(得分:1)
当创建派生类的实例时,堆分配将类似于(*):
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)
在实例化派生类的对象时,不会实例化基类的对象。继承只会将基类的某些属性和方法带到派生类。当制作/销毁派生类的对象时,基类的构造函数/析构函数与派生类的构造函数/析构函数一起被调用。但这并不意味着基类的对象也是如此。