Java中的继承 - 创建子类对象也会调用父类的构造函数。为什么?

26

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

我有两个类AB,并且类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!

问题:当我创建一个B类的对象时,为什么会调用A类构造函数

我知道B从A那里继承了所有的实例或类变量和所有方法,在这个意义上,B对象具有A的所有特征以及由B定义的其他一些特征。然而,我不知道并且也没有想象过当我创建一个B类型的对象时,A的构造函数也会被调用。

B b = new B();

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

这变得有趣了,

有人能解释一下为什么会发生这种情况吗?

15个回答

32

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

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

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

在编译器插入super调用后,您的子类实际上看起来像这样:

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

9
我认为这里的根本误解在于对于super()方法是否创建了一个新对象的理解。实际上并没有。 - Tim Frey

18

它不会创建两个对象,只会创建一个B类的实例。调用超类构造函数的原因是,像你说的那样,B具有A类的所有字段,这些字段需要被初始化。


它不是两个对象。如果用语言表达,可以说“B是A”。 汽车是一种汽车。它不是两个对象。 - John Gardner
类A需要一个初始化自身的机会。 - Steve Kuo
那么如果对象A没有被创建,如何访问类A的属性,这是如何工作的? - Notron

6

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

此外,考虑一下如果子类依赖于基类的某些内部状态会发生什么情况。难道您不希望初始化基类的实例吗?


3
这是因为构造函数用于初始化对象。由于B也是A的一种,它首先调用A的构造函数,然后再调用B的构造函数。
附带说明,您可以使用super(arg1,etc)根据传递的参数类型选择调用A的哪个构造函数...但它必须是构造函数中的第一行。

2
构造函数包含了A的所有初始化。你不是在创建两个对象,而是先创建一个对象,然后运行超类的初始化程序来初始化其成员,最后再运行派生类的初始化程序来初始化其成员。

2

创建B并不会额外创造A。

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

Java/C++会隐式地调用A的构造函数。为什么?这是语言设计的原因。但是这样做是可以的,因为A的构造函数可能包含一些初始化操作。由于B使用了A的所有特性和缺陷,这些特性最好被适当地初始化。


2

在大多数面向对象编程中,类的构造函数是非常重要的概念。

通过提供状态和操作该状态的手段,类允许更容易地维护不变量。构造函数的作用是将类置于符合这些不变量的状态(或抛出异常以禁止使用无效对象)。在许多语言中,这种限制比预期的宽松,因为构造函数允许将自己的“this”引用传递到其他地方,但至少这在类的控制范围内(因此它可以知道它处于足够稳定和有效的状态,以便对外界可访问)。

继承使这一过程变得复杂,因为B在很实际的意义上是A,因此可以调用A提供的任何方法。因此,在B开始真正的工作之前,应先初始化A的部分,因此会先调用A的构造函数,然后再轮到B的构造函数进行。


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的构造函数,我该如何获取它的默认值?

1
如果A在它的构造函数中初始化成员变量,而你在派生类中忘记调用super,那么A的成员变量可能处于不良状态。Java试图阻止你自己给自己惹麻烦。

1
当创建新对象B时,B类内部会创建A对象(因为使用了extends关键字)。在B类中,JVM会搜索B类构造函数,但由于extends关键字,它会转到超类构造函数。在A类中,x值被初始化。但是x是私有的,所以我们可以通过getXxx()方法从外部访问并获取结果。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接