在 Java 中调用构造函数
Calling of Constructors in a Java
书中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 的构造函数中
它演示了如何调用 subclass 的构造函数。但是为什么在没有 super() 构造函数的情况下调用 super class 的构造函数。
为什么 Java 语言设计者认为有必要这样做?
每个构造函数调用其超类构造函数。 super() 调用发生在构造函数的第一行。来自 javadoc:
If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the
no-argument constructor of the superclass. If the super class does not
have a no-argument constructor, you will get a compile-time error.
Object does have such a constructor, so if Object is the only
superclass, there is no problem.
更多here
因为它在 Java Language Specification.
中是这么说的
If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
"The Java programming language" 表示 "A constructor in subclass can initialize its individual state, however, as keeping contract, only super class knows how to initialize super class's state"。
因此,必须调用 super class 的构造函数。构造函数的处理顺序如下:
- 调用超级class构造函数
- 使用初始化程序和初始化块初始化字段
- 执行构造函数体
更多详细信息,请查看本书“3.2”部分。
正如其他人指出的那样,如果您不使用 super(...)
调用启动构造函数,编译器将为您调用 super()
。
至于原因,您必须首先记住构造函数的用途:初始化对象。具体是什么意思?实际上,这意味着为对象的字段赋值,并建立不变量。
如果不调用 super()
,B
和 A
classes 将没有机会为它们包含的任何字段执行此操作。如果这些字段是私有的,你甚至不能让 C()
构造函数为他们做这件事,因为私有字段在你的 class 之外是不可访问的(甚至你的超级 class' s 字段是可访问的)。即使你可以,这也不是一个好主意;它还会破坏封装。例如,想象一下,如果一个超级 class —— 可能是一个你不是其内部专家的复杂的超级 —— 突然决定改变它的实现细节,你不得不改变你的代码。
为了说明这一点,考虑一组非常简单的 classes:
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
字段将为空(引用类型的默认值)。但是 name()
方法不检查 null;它假定引用是非空的,因为构造函数建立了该不变量。因此,在我们不调用超级构造函数的伪 Java 中,Super.name
必须变得更复杂一些——它必须检查 name == null
.
您可以想象,随着 classes 获得更多字段,具有更多有趣的不变量,这个玩具示例会变得越来越复杂。强制您调用超级构造函数——无论是显式还是隐式——让超级的作者 class 建立他们的不变量,从而产生更简单、更可维护的代码。
继承基本上就是继承你parentclass的所有属性。因此,如果调用子 class 构造函数,它肯定会默认继承其所有 parent class 属性。在下面的代码中,class A
的所有属性也应该在 class B
中可用,所以如果我只调用 B 的构造函数,所有 class 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
想象一下 class C 访问 class B 或 A 的单元化变量。隐式调用 class B-->class A 的构造函数确保你是总是访问继承 classes(A or B)
的初始化变量
甚至它也与 Abstract classes 一起发挥作用。我们无法初始化抽象对象 class。但是Abstractclass的Childclass默认调用的是super()方法。所以抽象class构造函数可以初始化它的实例变量。
例如:
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
在这里你可以看到,当我们初始化 class TestB 的对象时,默认情况下超级构造函数正在调用并且 TestA 的构造函数正在分配 a 的值。如果 super 不会被默认调用,我们就不能为 abstract class 的实例变量赋值。
书中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 的构造函数中
它演示了如何调用 subclass 的构造函数。但是为什么在没有 super() 构造函数的情况下调用 super class 的构造函数。
为什么 Java 语言设计者认为有必要这样做?
每个构造函数调用其超类构造函数。 super() 调用发生在构造函数的第一行。来自 javadoc:
If a constructor does not explicitly invoke a superclass constructor, the Java compiler automatically inserts a call to the no-argument constructor of the superclass. If the super class does not have a no-argument constructor, you will get a compile-time error. Object does have such a constructor, so if Object is the only superclass, there is no problem.
更多here
因为它在 Java Language Specification.
中是这么说的If a constructor body does not begin with an explicit constructor invocation and the constructor being declared is not part of the primordial class Object, then the constructor body implicitly begins with a superclass constructor invocation "super();", an invocation of the constructor of its direct superclass that takes no arguments.
"The Java programming language" 表示 "A constructor in subclass can initialize its individual state, however, as keeping contract, only super class knows how to initialize super class's state"。
因此,必须调用 super class 的构造函数。构造函数的处理顺序如下:
- 调用超级class构造函数
- 使用初始化程序和初始化块初始化字段
- 执行构造函数体
更多详细信息,请查看本书“3.2”部分。
正如其他人指出的那样,如果您不使用 super(...)
调用启动构造函数,编译器将为您调用 super()
。
至于原因,您必须首先记住构造函数的用途:初始化对象。具体是什么意思?实际上,这意味着为对象的字段赋值,并建立不变量。
如果不调用 super()
,B
和 A
classes 将没有机会为它们包含的任何字段执行此操作。如果这些字段是私有的,你甚至不能让 C()
构造函数为他们做这件事,因为私有字段在你的 class 之外是不可访问的(甚至你的超级 class' s 字段是可访问的)。即使你可以,这也不是一个好主意;它还会破坏封装。例如,想象一下,如果一个超级 class —— 可能是一个你不是其内部专家的复杂的超级 —— 突然决定改变它的实现细节,你不得不改变你的代码。
为了说明这一点,考虑一组非常简单的 classes:
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
字段将为空(引用类型的默认值)。但是 name()
方法不检查 null;它假定引用是非空的,因为构造函数建立了该不变量。因此,在我们不调用超级构造函数的伪 Java 中,Super.name
必须变得更复杂一些——它必须检查 name == null
.
您可以想象,随着 classes 获得更多字段,具有更多有趣的不变量,这个玩具示例会变得越来越复杂。强制您调用超级构造函数——无论是显式还是隐式——让超级的作者 class 建立他们的不变量,从而产生更简单、更可维护的代码。
继承基本上就是继承你parentclass的所有属性。因此,如果调用子 class 构造函数,它肯定会默认继承其所有 parent class 属性。在下面的代码中,class A
的所有属性也应该在 class B
中可用,所以如果我只调用 B 的构造函数,所有 class 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
想象一下 class C 访问 class B 或 A 的单元化变量。隐式调用 class B-->class A 的构造函数确保你是总是访问继承 classes(A or B)
的初始化变量甚至它也与 Abstract classes 一起发挥作用。我们无法初始化抽象对象 class。但是Abstractclass的Childclass默认调用的是super()方法。所以抽象class构造函数可以初始化它的实例变量。
例如:
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 在这里你可以看到,当我们初始化 class TestB 的对象时,默认情况下超级构造函数正在调用并且 TestA 的构造函数正在分配 a 的值。如果 super 不会被默认调用,我们就不能为 abstract class 的实例变量赋值。