友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!阅读过程发现任何错误请告诉我们,谢谢!! 报告错误
一世书城 返回本书目录 我的书架 我的书签 TXT全本下载 进入书吧 加入书签

Java编程思想第4版[中文版](PDF格式)-第71章

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




      return 〃Greenhouse water is on〃;  

    }  

  }  

  private class WaterOff extends Event {  

    public WaterOff(long eventTime) {  

      super(eventTime);  

    }  

    public void action() {  

      // Put hardware control code here  

      water = false;  

    }  

    public String description() {  

      return 〃Greenhouse water is off〃;  

    }  

  }  

  private class ThermostatNight extends Event {  

    public ThermostatNight(long eventTime) {  

      super(eventTime);  

    }  

    public void action() {  

      // Put hardware control code here  

      thermostat = 〃Night〃;  

    }  

    public String description() {  

      return 〃Thermostat on night setting〃;  

    }  

  }  

  private class ThermostatDay extends Event {  

    public ThermostatDay(long eventTime) {  

      super(eventTime);  

    }  

    public void action() {  

      // Put hardware control code here  

      thermostat = 〃Day〃;  

    }  

    public String description() {  

      return 〃Thermostat on day setting〃;  

    }  

  }  

  // An example of an action() that inserts a   

  // new one of itself into the event list:  

  private int rings;  

  private class Bell extends Event {  

    public Bell(long eventTime) {  

      super(eventTime);  

    }  

    public void action() {  

      // Ring bell every 2 seconds; rings times:  

      System。out。println(〃Bing!〃);  

      if(……rings 》 0)  

        addEvent(new Bell(  



                                                                                        196 


…………………………………………………………Page 198……………………………………………………………

          System。currentTimeMillis() + 2000));  

    }  

    public String description() {  

      return 〃Ring bell〃;  

    }  

  }  

  private class Restart extends Event {  

    public Restart(long eventTime) {  

      super(eventTime);  

    }  

    public void action() {  

      long tm = System。currentTimeMillis();  

      // Instead of hard…wiring; you could parse  

      // configuration information from a text  

      // file here:  

      rings = 5;  

      addEvent(new ThermostatNight(tm));  

      addEvent(new LightOn(tm + 1000));  

      addEvent(new LightOff(tm + 2000));  

      addEvent(new WaterOn(tm + 3000));  

      addEvent(new WaterOff(tm + 8000));  

      addEvent(new Bell(tm + 9000));  

      addEvent(new ThermostatDay(tm + 10000));  

      // Can even add a Restart object!  

      addEvent(new Restart(tm + 20000));  

    }  

    public String description() {  

      return 〃Restarting system〃;  

    }  

  }  

  public static void main(String'' args) {  

    GreenhouseControls gc =   

      new GreenhouseControls();  

    long tm = System。currentTimeMillis();  

    gc。addEvent(gc。new Restart(tm));  

    gc。run();  

  }   

} ///:~  

  

注意 light (灯光)、water (供水)、thermostat (调温)以及rings 都隶属于外部类 

GreenhouseControls ,所以内部类可以毫无阻碍地访问那些字段。此外,大多数action()方法也涉及到某些 

形式的硬件控制,这通常都要求发出对非 Java 代码的调用。  

大多数Event 类看起来都是相似的,但 Bell (铃)和Restart (重启)属于特殊情况。Bell 会发出响声,若 

尚未响铃足够的次数,它会在事件列表里添加一个新的Bell 对象,所以以后会再度响铃。请注意内部类看起 

来为什么总是类似于多重继承:Bell 拥有Event 的所有方法,而且也拥有外部类GreenhouseControls 的所 

有方法。  

Restart 负责对系统进行初始化,所以会添加所有必要的事件。当然,一种更灵活的做法是避免进行“硬编 

码”,而是从一个文件里读入它们(第 10章的一个练习会要求大家修改这个例子,从而达到这个目标)。由 

于Restart()仅仅是另一个 Event 对象,所以也可以在Restart。action()里添加一个 Restart 对象,使系统 

能够定期重启。在main()中,我们需要做的全部事情就是创建一个 GreenhouseControls 对象,并添加一个 

Restart 对象,令其工作起来。  

这个例子应该使大家对内部类的价值有一个更加深刻的认识,特别是在一个控制框架里使用它们的时候。此 



                                                                                          197 


…………………………………………………………Page 199……………………………………………………………

外,在第 13 章的后半部分,大家还会看到如何巧妙地利用内部类描述一个图形用户界面的行为。完成那里的 

学习后,对内部类的认识将上升到一个前所未有的新高度。  



7。7 构建器和多形性  



同往常一样,构建器与其他种类的方法是有区别的。在涉及到多形性的问题后,这种方法依然成立。尽管构 

建器并不具有多形性(即便可以使用一种“虚拟构建器”——将在第 11 章介绍),但仍然非常有必要理解构 

建器如何在复杂的分级结构中以及随同多形性使用。这一理解将有助于大家避免陷入一些令人不快的纠纷。  



7。7。1  构建器的调用顺序  



构建器调用的顺序已在第4 章进行了简要说明,但那是在继承和多形性问题引入之前说的话。  

用于基础类的构建器肯定在一个衍生类的构建器中调用,而且逐渐向上链接,使每个基础类使用的构建器都 

能得到调用。之所以要这样做,是由于构建器负有一项特殊任务:检查对象是否得到了正确的构建。一个衍 

生类只能访问它自己的成员,不能访问基础类的成员(这些成员通常都具有private 属性)。只有基础类的 

构建器在初始化自己的元素时才知道正确的方法以及拥有适当的权限。所以,必须令所有构建器都得到调 

用,否则整个对象的构建就可能不正确。那正是编译器为什么要强迫对衍生类的每个部分进行构建器调用的 

原因。在衍生类的构建器主体中,若我们没有明确指定对一个基础类构建器的调用,它就会“默默”地调用 

默认构建器。如果不存在默认构建器,编译器就会报告一个错误(若某个类没有构建器,编译器会自动组织 

一个默认构建器)。  

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

  

//: Sandwich。java  

// Order of constructor calls  

  

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();  



                                                                                         198 


…………………………………………………………Page 200……………………………………………………………

  Cheese c = new Cheese();  

  Lettuce l = new Lettuce();  

  Sandwich() {   

    System。out。println(〃Sandwich()〃);  

  }  

  public static void main(String'' args) {  

    new Sandwich();  

  }  

} ///:~  

  

这个例子在其他类的外部创建了一个复杂的类,而且每个类都有一个构建器对自己进行了宣布。其中最重要 

的类是Sandwich ,它反映出了三个级别的继承(若将从Object 的默认继承算在内,就是四级)以及三个成 

员对象。在 main()里创建了一个 Sandwich 对象后,输出结果如下:  

  

Meal()  

Lunch()  

PortableLunch()  

Bread()  

Cheese()  

Lettuce()  

Sandwich()  

  

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

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

类,等等。直到抵达最深一层的衍生类。  

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

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

  

构建器调用的顺序是非常重要的。进行继承时,我们知道关于基础类的一切,并且能访问基础类的任何 

public 和 protected 成员。这意味着当我们在衍生类的时候,必须能假定基础类的所有成员都是有效的。采 

用一种标准方法,构建行动已经进行,所以对象所有部分的成员均已得到构建。但在构建器内部,必须保证 

使用的所有成员都已构建。为达到这个要求,唯一的办法就是首先调用基础类构建器。然后在进入衍生类构 

建器以后,我们在基础类能够访问的所有成员都已得到初始化。此外,所有成员对象(亦即通过合成方法置 

于类内的对象)在类内进行定义的时候(比如上例中的b,c 和 l),由于我们应尽可能地对它们进行初始 

化,所以也应保证构建器内部的所有成员均为有效。若坚持按这一规则行事,会有助于我们确定所有基础类 

成员以及当前对象的成员对象均已获得正确的初始化。但不幸的是,这种做法并不适用于所有情况,这将在 

下一节具体说明。  



7。7。2  继承
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!