父类的构造器总是在子类的构造过程中被调用,而且按照继承曾自逐渐向上链接,以使得每个父类的构造器都能得到调用。这样做是有意义的,因为构造器具有一项特殊任务:检查对象是否呗正确的构造。子类只能访问它自己的成员,不能访问父类中的成员(父类成员通常是private类型)。只有父类的构造器才具有恰当的知识和权限来对自己的元素进行初始化。因此,必须令所有构造器都得到调用,否则就不能正确的构造完整的对象。这正是编译器为什么要强制每个子类部分都必须调用构造器的原因。在子类的构造器主体中,如果没有明确指定调用某个父类构造器,它就会“默默”地调用默认构造器。如果不存在默认构造器,编译器就会报错。
让我们来看看例子,它展示组合、继承以及多态在构建顺序上的作用。
//: polymorphism/Sandwich.java
// Order of constructor calls.
package polymorphism;
import static net.mindview.util.Print.*;
class Meal {
Meal() { print("Meal()"); }
}
class Bread {
Bread() { print("Bread()"); }
}
class Cheese {
Cheese() { print("Cheese()"); }
}
class Lettuce {
Lettuce() { print("Lettuce()"); }
}
class Lunch extends Meal {
Lunch() { print("Lunch()"); }
}
class PortableLunch extends Lunch {
PortableLunch() { print("PortableLunch()");}
}
public class Sandwich extends PortableLunch {
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
public Sandwich() { print("Sandwich()"); }
public static void main(String[] args) {
new Sandwich();
}
} /* Output:
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
*///:~
在例子用,用其他类创建了一个复杂的类,而且每个类都有一个声明它自己的构造器。其中最重要的类是Sandwich,它反映了三层继承以及三个成员对象。当在main()里创建一个Sandwich对象后,就可以看到输出结果。
多态中构造器的调用数序总结如下:
- 调用父类构造器。这个步骤会不断地反复递归下去,首先是构造这种层次结构的根,然后是下一个子类,等等,直到最底层的子类。
- 按声明顺序调用成员的初始化方法。
- 调用子类构造器的主体。