在Java:完整的参考书
中// Demonstrate when constructors are called.
// Create a super class.
class A {
A() {
System.out.println("Inside A's constructor.");
}
}
// Create a subclass by extending class A.
class B extends A {
B() {
System.out.println("Inside B's constructor.");
}
}
// Create another subclass by extending B.
class C extends B {
C() {
System.out.println("Inside C's constructor.");
}
}
class CallingCons {
public static void main(String args[]) {
C c = new C();
}
}
输出: 在A的构造函数中 在B的构造函数里面 在C的构造函数中
它演示了如何调用子类的构造函数。但是为什么在没有super()构造函数的情况下调用超类的构造函数。
为什么Java语言设计师认为有必要这样做?
答案 0 :(得分:4)
每个构造函数都调用它的超类构造函数。 super()调用作为构造函数中的第一行发生。来自javadoc:
如果构造函数没有显式调用超类构造函数,Java编译器会自动插入一个调用 超类的无参数构造函数。如果超级班没有 有一个无参数的构造函数,你会得到一个编译时错误。 对象确实有这样的构造函数,所以如果Object是唯一的 超类,没有问题。
更多here
答案 1 :(得分:3)
正如其他人所指出的那样,如果你没有通过super(...)
调用启动构造函数,编译器会为你调用super()
。
至于 why ,你必须首先记住构造函数的用途:初始化对象。具体是什么意思?实际上,它意味着为对象的字段分配值,并建立不变量。
如果不调用super()
,B
和A
类就无法为其中包含的任何字段执行此操作。如果这些字段是私有的,您甚至不能让C()
构造函数为它们执行此操作,因为私有字段在您的课程外无法访问(甚至您的超级字段也无法访问)是可访问的)。即使你可以,它也不是一个好主意;它也会破坏封装。例如,想象一下如果一个超级类 - 可能是一个内部你不是专家的复杂类 - 突然决定改变它的实现细节,就必须改变你的代码。
为了说明这一点,请考虑一组非常简单的类:
public class Super {
private final String name;
Super() {
name = "default";
}
public String name() {
return name.toUpperCase();
}
}
public class Sub extends Super {
public Sub() {
// don't do anything
}
}
当您实例化Sub
时,它将通过调用Super
的构造函数开始。如果它没有,则name
字段将为null(引用类型的默认值)。但name()
方法不检查null;它假定引用为非null,因为构造函数建立了不变量。因此,在我们不会调用超级构造函数的伪Java中,Super.name
必须变得更复杂 - 它必须检查name == null
。
你可以想象,随着课程获得更多的领域,更多有趣的不变量,这个玩具的例子会变得越来越复杂。强制你调用超级构造函数 - 显式或隐式 - 让超类的作者建立不变量,从而产生更简单,更易维护的代码。
答案 2 :(得分:2)
因为它在Java Language Specification中说明了。
如果构造函数体不是以显式构造函数调用开始并且声明的构造函数不是原始类Object的一部分,那么构造函数体隐式地以超类构造函数调用“super();”开头,调用它的直接超类的构造函数,不带参数。
答案 3 :(得分:2)
即使它也有抽象类的作用。我们不能初始化抽象类的对象。但是,Abstract类的Abstract类默认调用super()方法。所以抽象类构造函数可以初始化它的实例变量 例如:
public abstract class TestA {
private int a;
public TestA()
{
a=10;
}
public int displayA()
{
return a;
}
abstract void display();
}
public class TestB extends TestA{
@Override
void display() {
System.out.println("this is class B");
}
}
package Abstract;
public class TestMain {
public static void main(String[] args) {
TestA obj= new TestB();
System.out.println(obj.displayA());
}
}
输出为:10 在这里你可以看到,当我们启动类TestB的对象时,默认情况下超级构造函数正在调用,而TestA的构造函数正在分配a的值。如果默认情况下不会调用super,则无法分配抽象类的实例变量。
答案 4 :(得分:1)
继承基本上是继承父类的所有属性。因此,如果调用子类构造函数,它肯定会默认继承其所有父类属性。在下面的代码中,class A
的所有属性也应该在class B
中可用,所以如果我只调用B的构造函数,所有A类的属性(私有除外)也会被初始化并可用,意思是B继承了A的属性
class A {
protected int a;
A() {
a=12;
System.out.println("Inside A's constructor.");
}
}
class B extends A {
B() {
System.out.println("Inside B's constructor.");
System.out.println(a);
}
}
public class ConstructorInheritance {
public static void main(String args[]) {
B b=new B();
}
}
output:
Inside A's constructor.
Inside B's constructor.
12
答案 5 :(得分:1)
想象一下C类访问B类或A类的单元化变量。隐式调用类B的构造函数 - >类A确保您始终访问继承类(A或B)的初始化变量
答案 6 :(得分:-1)
“Java编程语言”说“子类中的构造函数可以初始化其各自的状态,但是,作为保持契约,只有超类知道如何初始化超类的状态”。
因此,必须调用超类的构造函数。有序列如何处理构造函数:
有关详细信息,请查看本书的“3.2”部分。