用于基础类的构建器肯定在一个衍生类的构建器中调用,而且逐渐向上链接,使每个基础类使用的构建器都能得到调用。之所以要这样做,是由于构建器负有一项特殊任务:检查对象是否得到了正确的构建。

下面让我们看看一个例子,它展示了按构建顺序进行合成、继承以及多形性的效果:

class Meal 
{
  Meal() { System.out.println("Meal()"); }
}
class Bread 
{
  Bread() { System.out.println("Bread()"); }
}
class Cheese
 {
  Cheese() { System.out.println("Cheese()"); }
}
class Lettuce
{
  Lettuce() { System.out.println("Lettuce()"); }
}
class Lunch extends Meal
 {
  Lunch() { System.out.println("Lunch()");}
}
class PortableLunch extends Lunch 
{
  PortableLunch() {
  System.out.println("PortableLunch()");
  }
}
class Sandwich extends PortableLunch 
{
  Bread b = new Bread();
  Cheese c = new Cheese();
  Lettuce l = new Lettuce();
  Sandwich() {
    System.out.println("Sandwich()");
  }
  public static void main(String[] args) {
    new Sandwich();
  }
}

出结果如下:

Meal()

Lunch()

PortableLunch()

Bread()

Cheese()

Lettuce()

Sandwich()

这意味着对于一个复杂的对象,构建器的调用遵照下面的顺序:

(1) 调用基础类构建器。这个步骤会不断重复下去,首先得到构建的是分级结构的根部,然

   后是下一个衍生类,等等。直到抵达最深一层的衍生类。

(2) 按声明顺序调用成员初始化模块。

(3) 调用衍生构建器的主体。

构建器内部的多形性方法的行为

构建器应该构建当前类还是衍生类,这是一个问题,

如下例子:

abstract class Glyph
 {
  abstract void draw();
  Glyph() {
  System.out.println("Glyph() before draw()");
  draw();
  System.out.println("Glyph() after draw()");
  }
}
class RoundGlyph extends Glyph 
{
  int radius = 1;
  RoundGlyph(int r) {
  radius = r;
  System.out.println(
  "RoundGlyph.RoundGlyph(), radius = "+ radius);
  }
  void draw() {
  System.out.println("RoundGlyph.draw(), radius = " + radius);
  }
}
public class PolyConstructors 
{
  public static void main(String[] args) {
    new RoundGlyph(5);
  }
}

输出结果:

Glyph() before draw()

RoundGlyph.draw(), radius = 0

Glyph() after draw()

RoundGlyph.RoundGlyph(), radius = 5

上文部分的初始化顺序并不十分完整,而那是解决问题的关键所在。初始化的实际过程是这样的:

(1) 在采取其他任何操作之前,为对象分配的存储空间初始化成二进制零。

(2) 就象前面叙述的那样,调用基础类构建器。此时,被覆盖的draw()方法会得到调用(的

   确是在RoundGlyph构建器调用之前),此时会发现 radius的值为 0,这是由于步骤(1)

   造成的。

(3) 按照原先声明的顺序调用成员初始化代码。

(4) 调用衍生类构建器的主体。

因此,设计构建器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于private方法,它们自动具有final 属性)。这些方法不能被覆盖,所以不会出现上述潜在的问题。