Java语言中构造函数的调用
在《Java:完整参考》一书中Java语言中构造函数的调用,java,oop,constructor,Java,Oop,Constructor,在《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() {
// 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语言设计者认为有必要这么做?每个构造函数调用它的超类构造函数。super()调用作为构造函数中的第一行进行。从javadoc: 如果构造函数没有显式调用超类构造函数,Java编译器会自动插入对 没有超类的参数构造函数。如果超级类没有 如果没有参数构造函数,则会出现编译时错误。 对象确实有这样的构造函数,所以如果对象是唯一的 超类,没有问题 更多因为它在 如果构造函数体不是以显式构造函数调用开始,并且所声明的构造函数不是原始类对象的一部分,则构造函数体隐式地以超类构造函数调用“super();”开始,这是对其直接超类的构造函数的调用,不带任何参数 “Java编程语言”说“子类中的构造函数可以初始化其单独的状态,然而,作为契约,只有超类知道如何初始化超类的状态” 因此,必须调用超类的构造函数。构造函数处理的顺序如下:
- 调用超类构造函数
- 使用初始值设定项和初始化块初始化字段
- 构造函数的执行体
有关更多详细信息,请参阅本书的“3.2”部分。正如其他人所指出的,如果您不使用
super(…)
调用启动构造函数,编译器将为您调用super()
至于为什么,您必须首先记住构造函数的用途:初始化对象。这具体是什么意思?实际上,这意味着为对象的字段赋值,并建立不变量
如果不调用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;它假定引用为非null,因为构造函数建立了该不变量。因此,在不调用超级构造函数的伪Java中,super.name
必须变得更复杂一点——它必须检查name==null
您可以想象,随着类获得更多的字段和更多有趣的不变量,这个玩具示例可能会变得越来越复杂。强制您调用超级构造函数(显式或隐式)可以让该超类的作者建立其不变量,从而生成更简单、更可维护的代码。继承基本上是继承父类的所有属性。因此,如果调用了子类构造函数,它肯定会默认继承其所有父类属性。在下面的代码中,
class A
的所有属性也应该在class B
中可用,因此如果我只调用B的构造函数,所有class A的属性(private除外)也会初始化并可用,这意味着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
假设类C访问类B或A的单位化变量。隐式调用类B的构造函数-->类A确保始终访问继承类(A或B)的初始化变量,即使它也具有抽象类的角色。我们无法初始化抽象类的对象。但是抽象类的子类默认调用super()方法。因此抽象类构造函数可以初始化其实例变量。
例如:
公共抽象类TestA{
私人INTA;
公共遗嘱
{
a=10;
}
公共int displayA()
{
返回a;
}
抽象空显示();
}
公共类TestB扩展了TestA{
@凌驾
无效显示(){
System.out.println(“这是B类”);
}
}
包摘要;
公共类TestMain{
公共静态void main(字符串[]args){
TestA obj=新的TestB();
System.out.println(obj.displayA());
}
}
输出为:10
在这里您可以看到,当我们初始化类TestB的对象时,默认情况下超级构造函数正在调用,而TestA的构造函数正在分配a的值。如果super在默认情况下不被调用,我们就不能分配抽象类的实例变量 除非在子构造函数的第一条语句中显式调用
super(…)
,否则JVM会自动添加对默认父构造函数的调用。这就是所谓的构造器链接。你比我先做到了。。。有趣的是,教程与标准相比,使用了不同的措辞。为什么java语言设计者认为D是必要的?