25

我有一个关于 Java 继承的问题。

我有两个类AandB和类 B继承自 A:

public class A {
     public A() {
         System.out.println("Hi!");
     }
}


public class B extends A {
     public B() {
         System.out.println("Bye!");
     }

     public static void main(String[] args) {
         B b = new B();
     }
}

当我运行程序 B 时,输出是:

Hi!
Bye!

问题:为什么在我创建和对象时调用构造函数class Aclass B

我知道 B 继承了 A 的所有内容——所有实例或类变量以及所有方法,从这个意义上说,B 的对象具有 A 的所有特征以及 B 中定义的一些其他特征。但是,我不知道也没有想象一下,当我创建 B 类型的对象时,也会调用 A 的构造函数。所以,写这个:

B b = new B();

创建两个对象 - 一个是 B 类型,一个是 A 类型

这越来越有趣了,

有人可以解释为什么会发生这种情况吗?

4

15 回答 15

31

它不会创建两个对象,只有一个:B

从另一个类继承时,必须在构造函数中调用 super()。如果您不这样做,编译器将为您插入该调用,您可以清楚地看到。

调用超类构造函数是因为否则对象将处于未初始化状态,子类的开发人员可能不知道。

在编译器插入 super 调用后,您的子类实际上如下所示:

public class B extends A {
    public B() {
        super();
        System.out.println("Bye!");
    }
}
于 2009-01-28T18:28:16.483 回答
18

它没有创建2个对象,它只创建了B的一个实例。调用超类构造函数的原因是,就像你说的,B拥有A的所有字段,这些字段需要初始化。

于 2009-01-28T18:28:10.820 回答
6

请记住,继承是基类和子类之间的“是”关系,因此每次您拥有一个子类的实例时,根据定义,您也将拥有一个基类的实例(作为实例的一部分,而不是作为两个单独的实例)。为了正确初始化基类,调用构造函数。

此外,想想如果你的子类依赖于基类的某些内部状态会发生什么。你不希望基类的实例被初始化吗?

于 2009-01-28T18:27:38.417 回答
3

这样做是因为构造函数用于初始化对象。由于 B 也是 A,它首先调用 A 的构造函数,然后调用 B 的构造函数。

作为旁注,您可以super(arg1, etc)根据传递的参数类型来选择调用 A 的哪个构造函数......但它必须是构造函数中的第一行。

于 2009-01-28T18:30:00.240 回答
2

构造函数包含 A 的所有初始化。您没有创建两个对象。您正在创建一个对象,然后运行超类的初始化程序来初始化其成员,然后运行派生类的初始化程序来初始化其成员。

于 2009-01-28T18:28:36.090 回答
2

它不创建两个对象,它只创建一个对象 b. b 属于 B 类型和 A 类型。构造函数基本上是说这里是构造我所需要做的。因此,当您创建一个新的“B”实例时,您正在构建一个既是 B() 又是 A() 的对象。想象以下场景:

class Q {
  int i;
  public Q() {
    // set default value
    i= 99;
  }
}

class Z extends Q {
  public Z() {
  }
}

如果 Q 的构造函数没有被调用,我将如何获得它的默认值?

于 2009-01-28T18:29:36.480 回答
2

B的创建不会创建额外的A。

但是通过创造 B,你创造了一种 A,因为 BA。

Java/C++ 为您隐式调用 A 的构造函数。为什么?语言设计。但是这样做很好,因为 A 的构造函数可能包含一些初始化。由于 B 使用了 A 的所有功能和错误,因此这些功能最好正确初始化。

于 2009-01-28T18:30:49.207 回答
2

类的构造函数是大多数 OOP 中非常重要的概念

类通过提供状态和操作该状态的方法,可以更轻松地维护不变量。构造函数的作用是使类进入符合那些不变量的状态(或抛出从而禁止使用 invliad 对象)。这在许多语言中比预期的要宽松一些,因为允许构造函数在其他地方传递自己的“this”引用,但这至少在类的控制之下(因此它可以知道它处于足够稳定和有效的状态以便世界其他地方可以访问)

继承使这变得复杂,因为 B 在非常真实的意义上是 A,因此可以调用 A 提供的任何方法。因此,作为 A 的 B 的部分应该有机会在 B 进入之前初始化自己,因此A 的构造函数在 B 构造函数的实际工作开始之前被调用。

于 2009-01-28T18:31:02.123 回答
1

如果 A 在其构造函数中初始化成员并且您忘记在派生类中调用 super ,则 A 的成员可能处于错误状态。Java 正试图阻止你在自己的脚下开枪。

于 2009-01-28T18:28:31.927 回答
1

只创建一个对象,两个承包商都在同一个对象上运行。

原因很简单,因为您知道 B 具有 A 的所有变量和方法,因此如果 A 的某些变量需要初始化,以便 A 的方法可以工作,则必须有人对其进行初始化 - 并且有人是 A 的构造函数。

例如:

public class A {
     public A() {
        x = 1;
     }
     private int x;
     public int getX() {
        return x;
     }
}


public class B extends A {
     public B() {
     }

     public static void main(String[] args) {
         B b = new B();
         System.out.println(b.getX()); // should print 1 
     }
}
于 2009-01-28T18:29:46.027 回答
1

当新对象是 create(B) 时,内部 BA 对象被创建(因为 extends 关键字)。在 B 类 JVM 中搜索 B 类构造函数,但由于扩展关键字,它转到超类构造函数。在一个类 x 值被初始化。但是 x 是私有的,因此我们可以访问外部类的 throw getXxx()方法并获取结果。

于 2012-10-05T12:15:29.393 回答
0

当创建子类对象时,在内部它不是为超类对象创建的。但是应该为超类成员分配内存。

于 2014-11-04T05:27:54.020 回答
0

在java中,当你创建子类的对象时,总是调用父类的构造函数,因为Object类是每个超类的父类,当你调用Object类的构造函数时,只会创建你的对象,java不支持多重继承在类的情况下,如果你扩展任何其他类,那么你的子类和 Object 类之间的关系是通过 Parent 类,所以要调用 Object 类的构造函数,必须调用 Parent 类的构造函数。

于 2015-06-27T11:45:56.913 回答
0

每个超类都有一个构造函数,并且层次结构中的每个构造函数都在创建子类的对象时运行。

于 2018-08-05T08:56:05.663 回答
-3

如果未创建超类对象,那么子类如何访问超类非静态方法和变量。

我研究了非静态方法和变量只能通过对象访问..

于 2015-06-28T12:13:16.093 回答