为什么在声明子类的对象时会调用超类的构造函数? (JAVA)

时间:2011-08-24 09:13:22

标签: java object constructor superclass

考虑以下代码:

class Test {
    Test() {
        System.out.println("In constructor of Superclass");
    }

    int adds(int n1, int n2) {
        return(n1+n2);
    }

    void print(int sum) {
        System.out.println("the sums are " + sum);
    }
}


class Test1 extends Test {
    Test1(int n1, int n2) {
        System.out.println("In constructor of Subclass");
        int sum = this.adds(n1,n2);
        this.print(sum);
    }

    public static void main(String[] args) {
        Test1 a=new Test1(13,12);
        Test c=new Test1(15,14);
    }
}

如果我们在超类中有一个构造函数,它将由我们为子类构造的每个对象调用(例如,对象a用于类Test1调用Test1(int n1, int n2)和及其父Test())。

为什么会这样?

该程序的输出是:

  

在Superclass

的构造函数中      

在Subclass

的构造函数中      

总和是25

     

在Superclass

的构造函数中      

在Subclass

的构造函数中      

总和为29

18 个答案:

答案 0 :(得分:35)

因为它将确保在调用构造函数时,它可以依赖于其超类中的所有字段进行初始化。

here

中的3.4.4

答案 1 :(得分:19)

是。必须在构造派生类之前构造超类,否则可能无法初始化派生类中应该可用的某些字段。

一点点说明: 如果必须显式调用超类构造函数并传递一些参数:

baseClassConstructor(){
    super(someParams);
}

那么超级构造函数必须是派生构造函数的第一个方法调用。 例如,这将无法编译:

baseClassConstructor(){
     foo(); 
     super(someParams); // compilation error
}

答案 2 :(得分:8)

  

super()由编译器自动添加到每个类构造函数中。

正如我们所知,默认构造函数是由编译器自动提供的,但它也为第一个语句添加了super()。如果你正在创建自己的构造函数而你没有this()或super()作为第一个语句,编译器将提供super()作为构造函数的第一个语句

enter image description here

答案 3 :(得分:3)

Java类按以下顺序实例化:

(在上课时间)  按顺序的静态成员和静态初始化程序块的初始化程序     宣言。

(在每个新对象上)

  1. 为构造函数参数创建局部变量
  2. 如果构造函数以调用另一个构造函数开始 class,评估参数并递归到上一步。所有步骤 已完成该构造函数,包括进一步的递归 构造函数调用,然后继续。
  3. 如果上面没有构造超类,则构造 超类(如果未指定,则使用no​​-arg构造函数)。像#2一样, 完成超类的所有这些步骤,包括构建 在继续之前,它是超类。
  4. 实例变量和非静态初始化程序块的初始化程序,in 声明的顺序。
  5. 构造函数的其余部分。

答案 4 :(得分:2)

这就是Java的工作原理。如果创建子对象,则(隐式)调用超级构造函数。

答案 5 :(得分:1)

首先调用Super类的构造函数,因为程序中的所有方法首先出现在堆中,并且在编译之后它们存储到堆栈中,因为首先调用了超类构造函数。

答案 6 :(得分:1)

这里将test扩展到test1类,这意味着你可以访问test1中的所有测试方法和变量。请注意,只有在为内存分配内存时才能访问类方法或变量,并且需要一些默认或参数化的构造函数,因此编译器发现它正在扩展一个类,它将尝试查找超类构造函数,以便您可以访问其所有方法。

答案 7 :(得分:1)

我们知道必须在创建对象之前初始化类的成员变量(字段),因为这些字段表示对象的状态。如果这些字段明确没有被启动,那么编译器通过调用无参数默认构造函数隐含地提供它们的默认值。这就是为什么子类构造函数调用超类无参数默认构造函数或由编译器隐式调用。本地变量不是由编译器提供的默认值。

答案 8 :(得分:1)

我会尝试从不同的角度回答这个问题。

假设Java没有自动为您调用超级构造函数。如果继承该类,则必须隐式调用超级构造函数,或者自己重写它。这将要求你掌握超级类如何工作的内部知识,这是不好的。它还需要重写代码,这也是不好的。

我同意在幕后调用超级构造函数有点不直观。另一方面,我不确定他们怎么能以更直观的方式做到这一点。

答案 9 :(得分:1)

“如果构造函数没有显式调用超类构造函数,Java编译器会自动插入对超类的无参数构造函数的调用。如果超类没有无参构造函数,你将得到一个编译器-time error。对象确实有这样的构造函数,所以如果Object是唯一的超类,那就没问题。“ (来源:https://docs.oracle.com/javase/tutorial/java/IandI/super.html

答案 10 :(得分:1)

在子类的默认构造函数中有一个默认的super()调用。

 //Default constructor of subClass
    subClass() {
    super();
    }

答案 11 :(得分:1)

简单来说,如果超类具有参数化构造函数,则需要在子类构造函数的第一行显式调用super(params),否则所有超类构造函数都被调用,直到对象类为remhead。

答案 12 :(得分:1)

由于您将基类属性继承到派生类,因此可能存在某些情况,您的派生类构造函数需要一些基类变量来初始化其变量。首先,它必须初始化基类变量,然后导出类变量。这就是Java调用第一个基类构造函数,然后派生类构造函数的原因。

并且在初始化父类时初始化子类没有任何意义。

答案 13 :(得分:1)

当我们创建子类的对象时,它必须考虑超类中定义的所有成员函数和成员变量。可能会出现一些成员变量可能在某些超类构造函数中初始化的情况。 因此,当我们创建一个子类对象时,相应的继承树中的所有构造函数都以上下方式调用。

特别是当一个变量被定义为 protected 时,它总是可以在子类中访问,而不管该子类是否在同一个包中。现在从子类中调用一个超类函数来打印这个受保护变量的值(可以在超类的构造函数中初始化),我们必须得到正确的初始化值。然后调用所有超类构造函数。

内部Java在每个构造函数中调用super()。所以每个子类构造函数使用super()调用它的超类构造函数,因此它们以上下方式执行。

注意:可以覆盖函数而不是变量。

答案 14 :(得分:1)

将在派生类构造函数之前调用基类构造函数。这是有道理的,因为它保证在执行派生类的构造函数时正确构造基类。这允许您在构造派生类时使用基类中的一些数据。

答案 15 :(得分:1)

构造函数实现使对象准备好工作的逻辑。对象可以在私有字段中保存状态,因此只有其类的方法才能访问它们。因此,如果您希望子类的实例在调用构造函数之后真正准备好工作(即包括从基类继承的所有功能都可以),则必须调用基类的构造函数。

这就是系统以这种方式工作的原因。

自动调用基类的默认构造函数。如果要更改它,则必须通过在子类'构造函数的第一行中编写super()来显式调用基类的构造函数。

答案 16 :(得分:1)

子类继承了它的超类中的字段,而那些字段来构造/初始化(这是构造函数的通常目的:初始化类成员,以便实例按需要工作。我们知道有些人,但在那些糟糕的构造函数中有更多的功能......)

答案 17 :(得分:0)

父母退出第一!! 就像现实世界的孩子一样,没有父母就无法生存。 因此,首先初始化父类(SuperClass)对于在子类(Subclass)类中使用thrm非常重要。