Java 中的继承——创建子类的对象也会调用超类的构造函数。为啥?

Posted

技术标签:

【中文标题】Java 中的继承——创建子类的对象也会调用超类的构造函数。为啥?【英文标题】:Inheritance in Java - creating an object of the subclass invokes also the constructor of the superclass. Why exactly?Java 中的继承——创建子类的对象也会调用超类的构造函数。为什么? 【发布时间】:2010-10-04 01:48:36 【问题描述】:

我有一个关于 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!

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

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

B b = new B();

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

这越来越有趣了,

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

【问题讨论】:

【参考方案1】:

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

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

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

编译器插入 super 调用后,你的子类实际上是这样的:

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

【讨论】:

我认为这里的根本误解是对 super() 的调用是否正在创建一个新对象。不是。【参考方案2】:

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

【讨论】:

不是 2 个对象。如果用语言来表达,“B 是 A”。汽车就是汽车。它不是 2 个对象。 A 类需要一个机会来初始化自己。【参考方案3】:

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

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

【讨论】:

【参考方案4】:

这样做是因为构造函数用于初始化对象。因为B也是A,所以先调用A的构造函数,再调用B的构造函数。

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

【讨论】:

【参考方案5】:

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

【讨论】:

【参考方案6】:

它不创建两个对象,它只创建一个对象 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 的构造函数没有被调用,我将如何获得它的默认值?

【讨论】:

【参考方案7】:

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

但是通过创建 B,您创建了一种 A,因为 B 是一个 A。

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

【讨论】:

【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:

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

【讨论】:

【参考方案10】:

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

原因很简单,因为你知道 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 
     

【讨论】:

【参考方案11】:

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

【讨论】:

【参考方案12】:

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

【讨论】:

【参考方案13】:

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

【讨论】:

【参考方案14】:

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

【讨论】:

【参考方案15】:

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

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

【讨论】:

以上是关于Java 中的继承——创建子类的对象也会调用超类的构造函数。为啥?的主要内容,如果未能解决你的问题,请参考以下文章

Java 面向对象继承部分

Java基础-继承

201771010128王玉兰《面象对象程序设计(Java)》第七周学习总结

李清华201772020113《面向对象程序设计(Java)》第六周学习总结

类与继承

深度解析:java必须掌握的知识点——类的重用