Java 为什么我们在声明子类的对象时调用了超类的构造函数?(爪哇)

Java 为什么我们在声明子类的对象时调用了超类的构造函数?(爪哇),java,object,constructor,superclass,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); } } c

考虑以下代码:

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);
    }
}
如果我们在超类中有一个构造函数,它将被我们为子类构造的每个对象调用(例如,类
Test1
调用
Test1(int n1,int n2)
的对象
a
,以及它的父对象
Test()

为什么会发生这种情况

该程序的输出为:

在超类的构造函数中

在子类的构造函数中

总数是25

在超类的构造函数中

在子类的构造函数中

总数是29


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

是。必须先构造超类,然后才能构造派生类,否则派生类中应可用的某些字段无法初始化

请注意: 如果必须显式调用超级类构造函数并向其传递一些参数:

baseClassConstructor(){
    super(someParams);
}
然后超级构造函数必须是派生构造函数的第一个方法调用。 例如,这不会编译:

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

子类从它的超类继承字段,并且这些字段必须被构造/初始化(这是构造函数的通常用途:初始化类成员以便实例按要求工作。我们知道有些人,但这些糟糕的构造函数中有更多的功能…)

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

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


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

Java类按以下顺序实例化:

(在类加载时) 0静态成员和静态初始值设定项块的初始值设定项,顺序为 声明的内容

(在每个新对象上)

  • 为构造函数参数创建局部变量
  • 如果构造函数以调用另一个构造函数开始 类,计算参数并递归到上一步。所有步骤 为该构造函数完成,包括 构造函数调用,然后继续
  • 如果超类不是由上述方法构造的,则构造 超类(如果未指定,则使用无参数构造函数)。比如#2, 对超类执行所有这些步骤,包括构造 在继续之前,它是超类
  • 实例变量和非静态初始值设定项块的初始值设定项,在 申报顺序
  • 构造函数的其余部分

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


    请参见

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

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

    特别是当变量被定义为受保护时,无论子类是否在同一个包中,它总是可以在子类中访问。现在从子类开始,如果我们调用一个超类函数来打印这个受保护变量的值(可能在超类的构造函数中初始化),我们必须得到正确的初始化值。因此,所有超类构造函数都被调用

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


    注意:函数可以被重写,而不是变量

    由于您将基类属性继承到派生类中,因此在某些情况下,派生类构造函数可能需要一些基类变量来初始化其变量。所以首先它必须初始化基类变量,然后是派生类变量。这就是为什么Java首先调用基类构造函数,然后调用派生类构造函数


    而且在不初始化父类的情况下初始化子类也没有任何意义。

    中的超类构造函数首先被调用,因为程序中的所有方法首先出现在堆中,编译后存储在堆栈中,由于先调用哪个超类构造函数。

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

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

     //Default constructor of subClass
        subClass() {
        super();
        }
    
    super()由编译器自动添加到每个类构造函数中

    正如我们所知,默认构造函数是由编译器自动提供的,但它也会为第一条语句添加super()