假设我有这段代码:
class Animal {
int legs = 4;
int head = 1;
}
public class Dog extends Animal {
public static void main (String []args) {
Dog dog = new Dog();
}
}
我知道super()
隐式放在no-args构造函数的第一行,所以我知道Animal
构造函数将被执行,所以Animal
是将设置实例变量。
为了达到这个目的,我想了解一旦超级构造函数(Animal
)初始化了这些变量,那些实例变量将保存在Animal对象中或复制到子类({{ 1}})。
在第一种情况下,对象Dog
将由Animal
隐式实例化,并且每当实例super();
需要访问其中一个变量时,它将完成访问保留的变量在实例Dog
中(在后台创建)。或者第二种情况,如果将临时创建对象Animal,则将所有实例变量(在Animal
中)复制到Animal
实例,然后删除临时创建的Dog
实例。 / p>
我个人认为,例如Animal
对象将直接链接到直接连接到对象的Dog
对象。
是这样的吗?
答案 0 :(得分:16)
只有一个对象,它立即(从一开始就是)一个Dog
实例。它包含Dog
的所有字段(不是您有任何字段)和 Animal
的所有字段。
最初所有变量都将设置为默认值(0,null等)。然后当你到达每个类的构造函数体(在调用超类构造函数之后),执行实例变量初始化器,然后执行该类的构造函数体。
不需要复制,因为只有一个对象。例如,如果您要编写一个Animal
构造函数,如下所示:
public Animal() {
System.out.println(this.getClass());
}
...它将打印出Dog
,因为该对象已经是Dog
,即使它的Dog
构造函数尚未执行。
答案 1 :(得分:9)
Dog
扩展Animal
,head
,legs
变量不是私有的,因此您将从Dog
实例访问它们。
在实践中,会发生以下情况:
Dog
个实例,该实例也是Animal
head
,并在Animal
的属性之后)Dog
的隐式构造函数(它调用super()
)Animal
的隐式构造函数称为结果是一个对象,Dog
,但也隐含Animal
。
Dog
对象的外观(为了示例,让我们忘记Object
类)。我们假设Dog
也有public String name;
属性。
这是Dog实例的内存映射:
Addr Type Name Defined in:
------------------------
| 0 | int | legs | Animal
| 1 | int | head | Animal
| 2 | String | name | Dog
------------------------
Animal
,则可以通过地址1 访问head
作为变量,Dog
,则可以通过地址2 访问name
作为变量,Dog
,则可以通过地址1 head
作为变量
在Animal
类主体中运行的代码只能看到地址0-1
。在Dog
类中运行的代码可以访问地址0-2
。如果属性对于超类是private
,那么该子地址将被禁止。
这个内存映射可以非常容易地向下转换对象:使用相同的内存映射,只有处理(代码和可见性)不同。
我希望它能澄清一点。
答案 2 :(得分:2)
一旦超级构造函数初始化了这些变量 (动物),那些实例变量将保存在Animal中 对象或复制到子类(Dog)。
首先,sub-class
中的实例变量未覆盖。它们只是可见。它们不会被复制到Dog的实例中。您可以使用Dog的实例从Dog类访问它们,如果它们被标记为public,protected或no-modifier
答案 3 :(得分:1)
没有动物对象,只会创建一个Dog对象。 Dog对象有变量leg,head继承自Animal对象。他们将表现为Dog对象的成员。由于您在Dog中扩展Animal类,因此将调用Animal类构造函数。由于Animal隐式扩展了Object,因此将从Animal调用Object类构造函数。
Java如何处理这些调用是Java的内部调用,可能会因版本而异,但您应该保持相同的行为。