12

基类:

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变量的对象。

但是从调用基类的构造函数到调用派生类的构造函数的时间点如何/在哪里i存储在内存中?

基类是抽象类的情况会怎样。

如果我能知道在不同时间点内存中发生了什么,我将不胜感激。

如果我说了一些根本不正确的话,请告诉我。我真的很想知道这件事是如何运作的。

4

2 回答 2

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 在结构上链接的方式相同,它们在初始化期间也是链接的。如果不是这样,就像我最初示例中的女孩出生时没有任何其他我们知道的东西一样,根据定义,她也是。不可能。完全矛盾。于是,两个类的构造过程都进行了,包括:将字段初始化为“零”值(null对于引用和false对于boolean's),并执行一个构造函数(可以调用其他构造函数)。

在这种特殊情况下, fieldi被初始化,Inheritance构造函数被执行,TestInheritance' 的字段(如果有的话)将被初始化,然后 aTestInheritance的构造函数将被执行。我们如何才能准确地知道每个类的构造函数是什么?比较简单。引发一切的是new TestInheritance()。这显然表明TestInheritance' 的构造函数没有任何参数(存在;否则会出现编译错误)。但是,此构造函数没有显式调用超类的任何构造函数(通过关键字super(...))。正如我们在上面看到的,这是不可能的,编译器会自动插入等价于super(),即不带参数调用超类的构造函数 (Inheritance())。同样,这个构造函数存在,一切都很好。输出将是:

I am in base class 0
I am in derived class

没有参数的构造函数被称为“默认构造函数”主要有 3 个原因: - 它们通常非常简单,因为它们没有参数。- 当子类的构造函数没有显式调用任何超类构造函数时,它们会被自动调用。- 它们是为那些类自动提供的,无需程序员显式编写任何构造函数。在这种情况下,构造函数什么都不做,只进行初始化。

从我目前所读到的,只创建了一个对象——派生类中包含 i 变量的对象。

这并不完全正确。从物理上讲,确实只创建了一个对象,但它对应于与new( TestInheritance) 一起使用的类,在这种情况下恰好没有字段,但这无关紧要。事实上,两条输出线都被打印出来了。从概念上讲……我们已经提到了这一点。

但是从调用基类的构造函数到调用派生类的构造函数的时间点,我是如何/在哪里存储在内存中的?

new TestInheritance()执行时,在调用构造函数之前,甚至在执行初始化之前,发生的第一件事就是为整个对象分配内存。- 通常这个内存是“脏的”,这就是它需要初始化的原因。- TestInheritance'字段,其超类'字段等有空间。甚至每个字段在内存中对象内的位置都是预先知道的。

因此,即使在调用基类的构造函数之前,已经为i任何其他字段分配了内存,只是它们是“脏的”(未初始化)。

基类是抽象类的情况会怎样。

没有区别。唯一的问题是您不能自己创建抽象类的对象。您将它们创建为(具体)子类的一部分。这类似于一个新人类不能由他/她自己出生的事实。要么是女婴,要么是男婴。

如果我能知道在不同时间点内存中发生了什么,我将不胜感激。

我希望我做到了。

如果我说了一些根本不正确的话,请告诉我。我真的很想知道这件事是如何运作的。

没有什么“根本不正确”。

于 2013-08-22T22:42:57.233 回答
2

我认为这是继承混淆的一个常见领域。您似乎已经掌握了只创建了一个对象,这是正确的。

我建议您将基类中的实例变量也视为包含在任何超类中,就像基类中的任何公共或受保护方法都可以从超类中访问一样。

当一个对象被实例化时,java运行时会做它需要做的事情来为所有需要存储的东西分配存储——主要是实例变量。因此,基类中的实例变量可以被认为是内存块的一部分,它包含对象的所有实例变量,无论它们是在子类中还是在超类中声明的。

还有一个术语更正:在您的代码中,变量没有明确地“初始化”——我认为您要问的是“分配”,即变量在什么时候在内存中有空间。“初始化”意味着该变量已被赋予一个值,尽管 Java 非常擅长为其变量分配默认值,但我认为在这种情况下你们是男性分配。

于 2013-08-22T22:43:43.590 回答