构建器调用的顺序已在第章进行了简要说明但那是在继承和多形性问题引入之前说的话 用于基础类的构建器肯定在一个衍生类的构建器中调用而且逐渐向上链接使每个基础类使用的构建器都能得到调用之所以要这样做是由于构建器负有一项特殊任务检查对象是否得到了正确的构建一个衍生类只能访问它自己的成员不能访问基础类的成员(这些成员通常都具有private属性)只有基础类的构建器在初始化自己的元素时才知道正确的方法以及拥有适当的权限所以必须令所有构建器都得到调用否则整个对象的构建就可能不正确那正是编译器为什么要强迫对衍生类的每个部分进行构建器调用的原因在衍生类的构建器主体中若我们没有明确指定对一个基础类构建器的调用它就会默默地调用默认构建器如果不存在默认构建器编译器就会报告一个错误(若某个类没有构建器编译器会自动组织一个默认构建器) 下面让我们看看一个例子它展示了按构建顺序进行合成继承以及多形性的效果 //: Sandwichjava // Order of constructor calls class Meal { Meal() { Systemoutprintln(Meal()); } } class Bread { Bread() { Systemoutprintln(Bread()); } } class Cheese { Cheese() { Systemoutprintln(Cheese()); } } class Lettuce { Lettuce() { Systemoutprintln(Lettuce()); } } class Lunch extends Meal { Lunch() { Systemoutprintln(Lunch());} } class PortableLunch extends Lunch { PortableLunch() { Systemoutprintln(PortableLunch()); } } class Sandwich extends PortableLunch { Bread b = new Bread(); Cheese c = new Cheese(); Lettuce l = new Lettuce(); Sandwich() { Systemoutprintln(Sandwich()); } public static void main(String[] args) { new Sandwich(); } } ///:~ 这个例子在其他类的外部创建了一个复杂的类而且每个类都有一个构建器对自己进行了宣布其中最重要的类是Sandwich它反映出了三个级别的继承(若将从Object的默认继承算在内就是四级)以及三个成员对象在main()里创建了一个Sandwich对象后输出结果如下 Meal() Lunch() PortableLunch() Bread() Cheese() Lettuce() Sandwich() 这意味着对于一个复杂的对象构建器的调用遵照下面的顺序 () 调用基础类构建器这个步骤会不断重复下去首先得到构建的是分级结构的根部然后是下一个衍生类等等直到抵达最深一层的衍生类 () 按声明顺序调用成员初始化模块 () 调用衍生构建器的主体 构建器调用的顺序是非常重要的进行继承时我们知道关于基础类的一切并且能访问基础类的任何public和protected成员这意味着当我们在衍生类的时候必须能假定基础类的所有成员都是有效的采用一种标准方法构建行动已经进行所以对象所有部分的成员均已得到构建但在构建器内部必须保证使用的所有成员都已构建为达到这个要求唯一的办法就是首先调用基础类构建器然后在进入衍生类构建器以后我们在基础类能够访问的所有成员都已得到初始化此外所有成员对象(亦即通过合成方法置于类内的对象)在类内进行定义的时候(比如上例中的bc和l)由于我们应尽可能地对它们进行初始化所以也应保证构建器内部的所有成员均为有效若坚持按这一规则行事会有助于我们确定所有基础类成员以及当前对象的成员对象均已获得正确的初始化但不幸的是这种做法并不适用于所有情况这将在下一节具体说明 |