2014-10-05 144 views
0

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

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

+2

只有一个实例,即一个对象。然而,对象由两部分组成,一部分来自父代,另一部分来自它自己带来的东西。尽管如此,没有任何需要Oracle撰写关于此的文章。 – gd1 2014-10-05 11:30:31

+0

它就像一个洋葱 - 每一层都是一个类,最内层是Object,然后是每个超类,直到你到达“真实”类。 (这是绝大多数面向对象语言的工作原理。) – 2014-10-05 11:57:50

回答

1

当派生的类的一个实例创建堆分配将是这样的(*):

  • 标准JVM对象头(与指针Class对象DerivedClass
  • 类的实例字段Object
  • 类的实例字段BaseClass
  • 类的实例字段DerivedClass

所以实际上,如果你忽略DerivedClass实例字段,这个对象看起来非常类似于BaseClass的一个实例,并且JVM可以引用这个对象,就好像它是一个BaseClass的实例一样,并且没有困难这样做。

类似地,在用于DerivedClass类对象是一个“虚拟方法表”与:

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

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

但是当你仔细观察,你会看到,例如,该Object组指向hashValue方法指针实际上指向一个在BaseClass(如果BaseClass的覆盖hashValue)。因此,JVM可以将对象看作是Object,调用hashValue,然后无缝地获取BaseClass(或DerivedClass,如果它也覆盖该方法)中的方法。 (*)在实践中,实例字段可能会混杂到一定程度,因为来自超类的“对齐”字段可能会在来自子类的字段填充的堆分配中留下间隙。这只是最小化对象大小的一个技巧。

2

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

  • 显式地在你的类定义中。
  • 在层次结构树的任何节点上。

是的,超类的构造函数被调用,但只是初始化那些在你的构造函数中声明的属性。它绝不意味着创建超类的新对象。

检查这些链接,他们可以帮助你理解这个过程:

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

+0

需要了解在分配对象之后调用“构造函数”*并不会导致*分配。因此,当您执行'新的SubClass()'时,SubClass和SuperClass(和Object)的空间被分配为一个“块”,所有类的实例变量一个接一个排列。然后调用构造函数为每个类初始化实例变量。 – 2014-10-05 12:14:13

+0

(如果你看字节码,有一个'new'操作,分配对象,然后调用'invokespecial'来调用构造函数。'new'操作查找“最外层”类描述来查找* entire *对象的大小(包括所有超类)并分配那么多存储空间。) – 2014-10-05 12:18:32

+0

@HotLicks这就是我试图解释的。在第二个链接中有很好的解释。我的回答传达他是否相反? (“第二个链接的网站指出:是新创建的对象,即:它为所有类的引用保留内存”) – 2014-10-05 12:19:12

0

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

+0

是的,没有。派生类实例内几乎是基类实例的所有组件。 – 2014-10-05 13:14:18

+0

虽然它包含基类的所有属性和方法,但它不是基类的对象,它是真的,但派生类的对象。 – 2014-10-05 13:25:49

+0

不完全正确。如果询问它是否是'instanceof'基类,派生类的一个对象将返回'true'。 – 2014-10-05 13:29:35

1

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