基类:
public class Inheritance {
int i;
Inheritance() {
System.out.println("I am in base class" + i);
}
}
派生类:
public class TestInheritance extends Inheritance {
TestInheritance() {
System.out.println("I am in derived class");
}
public static void main(String[] args) {
TestInheritance obj = new TestInheritance();
}
}
这就是我对上面发生的事情的想法。
当我默认创建派生类的对象时,会调用super()
并调用基类的构造函数,并初始化变量i
。
现在,我的问题是:在这种情况下,构造函数是否只初始化变量i
并且不创建类的具体对象?
从我到目前为止所读到的内容中只创建了一个对象 - 派生类中包含i
变量。
但是从调用基类的构造函数和调用派生类的构造函数的时间点开始,如何/ {where i
存储在内存中?
基类是抽象基类的情况。
如果我能知道在不同的时间点内存会发生什么,我真的很感激。
如果我说了一些根本不正确的话,请告诉我。我真的想知道这件事是如何运作的。
答案 0 :(得分:4)
在物理上你创造了一个单一的物体,但从概念上你创造了2.它就像一个女婴出生的时候一样:身体上她只有一个,但从概念上讲,一个新的女性已经出生,也是一个新的人类已经诞生,一个新的有感觉的生命已经诞生,地球的新居民已经诞生,等等,确切地说。这里也存在继承关系(subTyping),我故意这样做。为了避免混淆,可能更好地说只有一个具有多个方面的对象;一个对象同时是不同(超级)类的成员。
现在,关于逻辑部分的说法已经足够了,让我们检查一下这个部分。单个(物理)对象在内存中使用以下结构创建
+--------------------------+----------------------------+
| B | C' +
+--------------------------+----------------------------+
第一部分(B)包含从C的超类B继承的所有字段(如果有的话)。第二部分(C'和我使用'
进行"补充")包含所有"专有" C(即不是从B继承而是在C本身中定义)。重要的是要注意,创建的对象不是在C'上开始,而是在B上开始。它是继承的字段与构成完整对象的新字段的组合。
现在,如果B有自己的超类A,那么结构将是这样的:
+--------+-----------------+----------------------------+
| A | B' | C' +
+--------+-----------------+----------------------------+
这里需要注意的是B == A + B'
。换句话说,C并不需要知道A.所有它关心的是它的直接超类B,它隐藏了它的内部结构。
回答具体问题:
在这种情况下构造函数是否只初始化变量i并且没有创建类的具体对象?
与上面的A,B和C在结构上链接的方式相同,它们在初始化期间也被链接。如果不是这样的话,就像我原来的例子中的女孩出生时没有任何其他我们知道的事情,她也按照定义< / strong>即可。这是不可能的。完全矛盾。因此,执行两个类的构建过程,其中包括:初始化&#34;零&#34;值(引用的null
和false
的{{1}},以及执行一个构造函数(可以调用其他构造函数)。
在这种特殊情况下,字段boolean
已初始化,i
构造函数已执行,Inheritance
字段(如果已有)将初始化,然后一个TestInheritance
的构造函数将被执行。我们怎样才能确切地说出每个类的构造函数?比较简单。启动一切的是TestInheritance
。这显然表明new TestInheritance()
的构造函数没有任何参数(存在;否则会出现编译错误)。但是,此构造函数未显式调用超类的任何构造函数(通过关键字TestInheritance
)。正如我们上面所看到的,这不可能,并且编译器会自动将等效项插入super(...)
,即调用超类的构造函数而不带参数(super()
)。同样,这个构造函数存在,一切都很好。产出应该是:
Inheritance()
没有参数的构造函数被称为&#34;默认构造函数&#34;主要原因有三个: - 它们通常非常简单,因为它们没有参数。 - 当子类的构造函数没有显式调用任何supeclass构造函数时,它们会自动调用。 - 它们是为这些类自动提供的,而没有程序员明确编写的任何构造函数。在这种情况下,构造函数不执行任何操作,只进行初始化。
从我到目前为止所读到的内容中只创建了一个对象 - 派生类中有i变量。
这不完全正确。在物理上,确实只创建了一个对象,但它对应于I am in base class 0
I am in derived class
(new
)使用的类,在这种情况下恰好没有字段,但这是无关紧要的。实际上,两个输出线都是打印出来的。从概念上来说......我们已经提到了这一点。
但是从调用基类的构造函数和调用派生类的构造函数的时间点开始如何/在哪里存储?
执行TestInheritance
时,在执行初始化之前,在调用构造函数之前发生的第一件事是为整个对象分配内存。
- 通常这个记忆是&#34;脏&#34;这就是它需要初始化的原因。
- new TestInheritance()
字段有空间,超级类别&#39;字段,等等。甚至事先知道记忆中物体内每个场的位置。
因此,甚至在调用基类的构造函数之前,已经为TestInheritance
和任何其他字段分配了内存,只是它们是&#34;脏&#34; (未初始化)。
基类是抽象基类的情况。
没有区别。唯一的一个是你不能自己创建抽象类的对象。您将它们创建为(具体)子类的一部分。这类似于一个新的人不能由他/她自己出生的事实。它是女婴还是男婴。
如果我能知道在不同的时间点内存会发生什么,我真的很感激。
我希望我做到了。
如果我说了一些根本不正确的话,请告诉我。我真的想知道这件事是如何运作的。
没什么&#34;根本不正确&#34;。
答案 1 :(得分:2)
我认为这是继承混淆的常见领域。您似乎已经掌握了只创建了一个对象,这是正确的。
我建议您将基类中的实例变量视为包含在任何超类中,就像从超类中可以访问基类中的任何公共或受保护方法一样。
当一个对象被实例化时,java运行时会做它需要做的事情来为所有需要存储的东西分配存储 - 主要是实例变量。因此,基类中的实例变量可以被认为是包含对象的所有实例变量的内存块的一部分,无论它们是在子类还是超类中声明。
一个术语修正:在你的代码中,变量没有明确地“初始化” - 我认为你要问的是“分配”,即变量在内存中的空间。 “Initialize”意味着变量已被赋予一个值,尽管Java非常善于为其变量分配默认值,但我认为在这种情况下你可以分配。