为啥在声明子类的对象时会调用超类的构造函数? (爪哇)
Posted
技术标签:
【中文标题】为啥在声明子类的对象时会调用超类的构造函数? (爪哇)【英文标题】:Why is constructor of super class invoked when we declare the object of sub class? (Java)为什么在声明子类的对象时会调用超类的构造函数? (爪哇) 【发布时间】:2011-11-02 15:13:25 【问题描述】:考虑这段代码:
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)
以及它的父类@987654325 @)。
为什么会这样?
这个程序的输出是:
在超类的构造函数中
在子类的构造函数中
总和是 25
在超类的构造函数中
在子类的构造函数中
总和是 29
【问题讨论】:
【参考方案1】:这就是 Java 的工作原理。如果创建子对象,则(隐式)调用超级构造函数。
【讨论】:
确切地说是默认的超级构造函数。如果在超类中没有定义默认构造函数并且您没有在子类的构造函数中显式调用 super(args)(第一行),则编译将失败。【参考方案2】:是的。必须在构造派生类之前构造超类,否则无法初始化派生类中应该可用的某些字段。
一个小提示: 如果你必须显式调用超类构造函数并传递一些参数:
baseClassConstructor()
super(someParams);
那么超级构造函数必须是派生构造函数的第一个方法调用。 例如,这不会编译:
baseClassConstructor()
foo();
super(someParams); // compilation error
【讨论】:
我认为您第二句话中的第二个“超级”是“派生的”。 (“必须先构造超类,然后才能构造 super 类...”)【参考方案3】:子类从它的超类继承字段,并且这些字段具有来构造/初始化(这是构造函数的通常目的:初始化类成员,以便实例按要求工作。我们知道有些人在那些糟糕的构造函数中提供了更多功能......)
【讨论】:
【参考方案4】:构造函数实现使对象准备好工作的逻辑。对象可以在私有字段中保存状态,因此只有其类的方法可以访问它们。因此,如果您希望子类的实例在调用构造函数后真正准备好工作(即它的所有功能,包括从基类继承的都可以),则必须调用基类的构造函数。
这就是系统以这种方式工作的原因。
自动调用基类的默认构造函数。如果你想改变这个,你必须通过在你的子类构造函数的第一行写super()
来显式调用基类的构造函数。
【讨论】:
【参考方案5】:Java 类按以下顺序实例化:
(在类加载时) 0. 静态成员的初始化器和静态初始化器块,按顺序 声明。
(在每个新对象处)
-
为构造函数参数创建局部变量
如果构造函数以调用另一个构造函数开始
类,评估参数并递归到上一步。所有步骤
为该构造函数完成,包括进一步递归
构造函数调用,然后继续。
如果上面没有构造超类,构造
超类(如果未指定,则使用无参数构造函数)。像#2一样,
为超类完成所有这些步骤,包括构建
这是超类,在继续之前。
实例变量和非静态初始化块的初始化器,在
声明顺序。
构造函数的其余部分。
【讨论】:
【参考方案6】:因为它将确保在调用构造函数时,它可以依赖于正在初始化的超类中的所有字段。
请参阅here 中的 3.4.4
【讨论】:
【参考方案7】:基类构造函数将在派生类构造函数之前调用。这是有道理的,因为它保证了在执行派生类的构造函数时正确构造了基类。这允许您在构造派生类期间使用基类中的一些数据。
【讨论】:
【参考方案8】:当我们创建一个子类的对象时,它必须考虑到父类中定义的所有成员函数和成员变量。可能会出现某些成员变量可能在某些超类构造函数中初始化的情况。 因此当我们创建一个子类对象时,对应继承树中的所有构造函数都以自顶向下的方式调用。
特别是当一个变量被定义为protected时,它总是可以在子类中访问无论子类是否在同一个包中。现在,如果我们从子类调用超类函数来打印这个受保护变量的值(可能在超类的构造函数中初始化),我们必须得到正确的初始化值。因此调用所有超类构造函数。
Java 内部在每个构造函数中调用 super()。所以每个子类构造函数都使用 super() 调用它的超类构造函数,因此它们以自上而下的方式执行。
注意:函数可以被覆盖而不是变量。
【讨论】:
【参考方案9】:由于您将基类属性继承到派生类中,因此在某些情况下,您的派生类构造函数可能需要某些基类变量来初始化其变量。所以首先它必须初始化基类变量,然后是派生类变量。这就是为什么 Java 首先调用基类构造函数,然后调用派生类构造函数。
而且在不初始化父类的情况下初始化子类也没有任何意义。
【讨论】:
【参考方案10】:首先调用超类的构造函数,因为程序中的所有方法首先出现在堆中,编译后存储到堆栈中,因此首先调用超类构造函数。
【讨论】:
【参考方案11】:简单来说,如果超类具有参数化构造函数,则需要在子类构造函数的第一行显式调用 super(params),否则会隐式调用所有超类构造函数,直到达到对象类。
【讨论】:
【参考方案12】:在你的子类的默认构造函数中有一个默认的 super() 调用。
//Default constructor of subClass
subClass()
super();
【讨论】:
【参考方案13】:super() 由编译器自动添加到每个类的构造函数中。
众所周知,默认构造函数是由编译器自动提供的,但它也会为第一条语句添加 super()。如果您正在创建自己的构造函数并且没有 this() 或 super() 作为第一条语句,编译器会提供 super() 作为构造函数的第一条语句。
【讨论】:
【参考方案14】:"如果构造函数没有显式调用超类构造函数,Java编译器会自动插入对超类无参数构造函数的调用。如果超类没有无参数构造函数,你会得到一个编译-time 错误。Object 确实有这样的构造函数,所以如果 Object 是唯一的超类,则没有问题。" (来源:https://docs.oracle.com/javase/tutorial/java/IandI/super.html)
【讨论】:
答案没说为什么?主要问题。【参考方案15】:我会尝试从不同的角度来回答这个问题。
假设 Java 没有自动为您调用超级构造函数。如果您继承该类,则必须隐式调用超级构造函数,或者自己重写它。这将要求您对超类的工作原理有内部了解,这很糟糕。它还需要重写代码,这也不好。
我同意在幕后调用超级构造函数有点不直观。另一方面,我不确定他们如何以更直观的方式做到这一点。
【讨论】:
【参考方案16】:众所周知,类的成员变量(字段)必须在创建对象之前进行初始化,因为这些字段代表对象的状态。如果这些字段没有被显式初始化,那么编译器会通过调用无参数默认构造函数来隐式地为它们提供默认值。这就是为什么子类构造函数调用超类无参数默认构造函数或由编译器隐式调用的原因。编译器不提供局部变量的默认值。
【讨论】:
【参考方案17】:在这里,您将 Test 扩展到您的 test1 类,这意味着您可以访问 test1 中的所有测试方法和变量。请注意,只有在为它分配了内存的情况下,你才能访问一个类方法或变量,并且它需要一些默认或参数化的构造函数,所以在这里编译器发现它正在扩展一个类,它会尝试找到超类构造函数,以便您可以访问其所有方法。
【讨论】:
【参考方案18】:父母先退出!! 和现实世界一样,没有父母,孩子就不可能存在。 因此,首先初始化父母(超类)对于在子类(子类)中使用 thrm 很重要..
【讨论】:
以上是关于为啥在声明子类的对象时会调用超类的构造函数? (爪哇)的主要内容,如果未能解决你的问题,请参考以下文章
java中,当实例化子类时会递归调用父类中的构造方法。这个说法对么?为啥