由于现在涉及基类和导出类这两个类,而不是只有一个类,所以要想象导出类所产生的结果
对象,会有点令人迷惑。从外部来看,它就像是一个与基类具有相同接口的新类,或许还会
有一些额外的方法和数据成员。但继承并不只是复制基类的接口。当你创建了一个导出类的
对象时,该对象包含了一个基类的子对象(subobject)。这个子对象与你用基类直接创建的
对象是一样的。二者区别在于,后者来自于外部,而基类的子对象被包装在导出类对象内部。
当然,对基类子对象的正确初始化也是至关重要的,而且也仅有一种方法来保证这一点:在
构造器中调用具有执行基类初始化所需要的所有知识和能力的基类构造器来执行初始化。
Java 会自动在导出类的构造器中插入对基类构造器的调用。下例展示了上述机制在三层继承
关系上是如何工作的:
//: c06:Cartoon.java
// Constructor calls during inheritance.
import com.bruceeckel.simpletest.*;
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing {
private static Test monitor = new Test();
public Cartoon() {
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
monitor.expect(new String[] {
"Art constructor",
"Drawing constructor",
"Cartoon constructor"
});
}
} ///:~
你会发现,构建过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前,
就已经完成了初始化。即使你不为 Cartoon()创建构造器,编译器也会为你合成一个缺省的
对象,会有点令人迷惑。从外部来看,它就像是一个与基类具有相同接口的新类,或许还会
有一些额外的方法和数据成员。但继承并不只是复制基类的接口。当你创建了一个导出类的
对象时,该对象包含了一个基类的子对象(subobject)。这个子对象与你用基类直接创建的
对象是一样的。二者区别在于,后者来自于外部,而基类的子对象被包装在导出类对象内部。
当然,对基类子对象的正确初始化也是至关重要的,而且也仅有一种方法来保证这一点:在
构造器中调用具有执行基类初始化所需要的所有知识和能力的基类构造器来执行初始化。
Java 会自动在导出类的构造器中插入对基类构造器的调用。下例展示了上述机制在三层继承
关系上是如何工作的:
//: c06:Cartoon.java
// Constructor calls during inheritance.
import com.bruceeckel.simpletest.*;
class Art {
Art() {
System.out.println("Art constructor");
}
}
class Drawing extends Art {
Drawing() {
System.out.println("Drawing constructor");
}
}
public class Cartoon extends Drawing {
private static Test monitor = new Test();
public Cartoon() {
System.out.println("Cartoon constructor");
}
public static void main(String[] args) {
Cartoon x = new Cartoon();
monitor.expect(new String[] {
"Art constructor",
"Drawing constructor",
"Cartoon constructor"
});
}
} ///:~
你会发现,构建过程是从基类“向外”扩散的,所以基类在导出类构造器可以访问它之前,
就已经完成了初始化。即使你不为 Cartoon()创建构造器,编译器也会为你合成一个缺省的
构造器,该构造器将调用基类的构造器。