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

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

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




RoundGlyph。draw(); radius = 0  

Glyph() after draw()  

RoundGlyph。RoundGlyph(); radius = 5  

  

当Glyph 的构建器调用draw()时,radius 的值甚至不是默认的初始值1,而是 0。这可能是由于一个点号或 

者屏幕上根本什么都没有画而造成的。这样就不得不开始查找程序中的错误,试着找出程序不能工作的原 

因。  

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

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

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

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

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

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

  

采取这些操作要求有一个前提,那就是所有东西都至少要初始化成零(或者某些特殊数据类型与“零”等价 

的值),而不是仅仅留作垃圾。其中包括通过“合成”技术嵌入一个类内部的对象句柄。如果假若忘记初始 

化那个句柄,就会在运行期间出现违例事件。其他所有东西都会变成零,这在观看结果时通常是一个严重的 

警告信号。  

在另一方面,应对这个程序的结果提高警惕。从逻辑的角度说,我们似乎已进行了无懈可击的设计,所以它 



                                                                                           203 


…………………………………………………………Page 205……………………………………………………………

的错误行为令人非常不可思议。而且没有从编译器那里收到任何报错信息(C++在这种情况下会表现出更合理 

的行为)。象这样的错误会很轻易地被人忽略,而且要花很长的时间才能找出。  

因此,设计构建器时一个特别有效的规则是:用尽可能简单的方法使对象进入就绪状态;如果可能,避免调 

用任何方法。在构建器内唯一能够安全调用的是在基础类中具有final 属性的那些方法(也适用于private 

方法,它们自动具有final 属性)。这些方法不能被覆盖,所以不会出现上述潜在的问题。  



7。8 通过继承进行设计  



学习了多形性的知识后,由于多形性是如此“聪明”的一种工具,所以看起来似乎所有东西都应该继承。但 

假如过度使用继承技术,也会使自己的设计变得不必要地复杂起来。事实上,当我们以一个现成类为基础建 

立一个新类时,如首先选择继承,会使情况变得异常复杂。  

一个更好的思路是首先选择“合成”——如果不能十分确定自己应使用哪一个。合成不会强迫我们的程序设 

计进入继承的分级结构中。同时,合成显得更加灵活,因为可以动态选择一种类型(以及行为),而继承要 

求在编译期间准确地知道一种类型。下面这个例子对此进行了阐释:  

  

//: Transmogrify。java  

// Dynamically changing the behavior of  

// an object via position。  

  

interface Actor {  

  void act();  

}  

  

class HappyActor implements Actor {  

  public void act() {   

    System。out。println(〃HappyActor〃);   

  }  

}  

  

class SadActor implements Actor {  

  public void act() {   

    System。out。println(〃SadActor〃);  

  }  

}  

  

class Stage {  

  Actor a = new HappyActor();  

  void change() { a = new SadActor(); }  

  void go() { a。act(); }  

}  

  

public class Transmogrify {  

  public static void main(String'' args) {  

    Stage s = new Stage();  

    s。go(); // Prints 〃HappyActor〃  

    s。change();  

    s。go(); // Prints 〃SadActor〃  

  }  

} ///:~  

  

在这里,一个Stage 对象包含了指向一个Actor 的句柄,后者被初始化成一个 HappyActor 对象。这意味着 

go()会产生特定的行为。但由于句柄在运行期间可以重新与一个不同的对象绑定或结合起来,所以SadActor 

对象的句柄可在a 中得到替换,然后由go()产生的行为发生改变。这样一来,我们在运行期间就获得了很大 



                                                                                           204 


…………………………………………………………Page 206……………………………………………………………

的灵活性。与此相反,我们不能在运行期间换用不同的形式来进行继承;它要求在编译期间完全决定下来。  

一条常规的设计准则是:用继承表达行为间的差异,并用成员变量表达状态的变化。在上述例子中,两者都 

得到了应用:继承了两个不同的类,用于表达 act()方法的差异;而 Stage 通过合成技术允许它自己的状态 

发生变化。在这种情况下,那种状态的改变同时也产生了行为的变化。  



7。8。1  纯继承与扩展  



学习继承时,为了创建继承分级结构,看来最明显的方法是采取一种“纯粹”的手段。也就是说,只有在基 

础类或“接口”中已建立的方法才可在衍生类中被覆盖,如下面这张图所示:  

  



                                        

  

可将其描述成一种纯粹的“属于”关系,因为一个类的接口已规定了它到底“是什么”或者“属于什么”。 

通过继承,可保证所有衍生类都只拥有基础类的接口。如果按上述示意图操作,衍生出来的类除了基础类的 

接口之外,也不会再拥有其他什么。  

可将其想象成一种“纯替换”,因为衍生类对象可为基础类完美地替换掉。使用它们的时候,我们根本没必 

要知道与子类有关的任何额外信息。如下所示:  

  



                                             

  

也就是说,基础类可接收我们发给衍生类的任何消息,因为两者拥有完全一致的接口。我们要做的全部事情 

就是从衍生上溯造型,而且永远不需要回过头来检查对象的准确类型是什么。所有细节都已通过多形性获得 

了完美的控制。  

若按这种思路考虑问题,那么一个纯粹的“属于”关系似乎是唯一明智的设计方法,其他任何设计方法都会 

导致混乱不清的思路,而且在定义上存在很大的困难。但这种想法又属于另一个极端。经过细致的研究,我 

们发现扩展接口对于一些特定问题来说是特别有效的方案。可将其称为“类似于”关系,因为扩展后的衍生 

类“类似于”基础类——它们有相同的基础接口——但它增加了一些特性,要求用额外的方法加以实现。如 

下所示:  

  



                                                                       205 


…………………………………………………………Page 207……………………………………………………………

                               

  

尽管这是一种有用和明智的做法(由具体的环境决定),但它也有一个缺点:衍生类中对接口扩展的那一部 

分不可在基础类中使用。所以一旦上溯造型,就不可再调用新方法:  

  



                                            

  

若在此时不进行上溯造型,则不会出现此类问题。但在许多情况下,都需要重新核实对象的准确类型,使自 

己能访问那个类型的扩展方法。在后面的小节里,我们具体讲述了这是如何实现的。  



7。8。2  下溯造型与运行期类型标识  



由于我们在上溯造型(在继承结构中向上移动)期间丢失了具体的类型信息,所以为了获取具体的类型信 

息——亦即在分级结构中向下移动——我们必须使用  “下溯造型”技术。然而,我们知道一个上溯造型肯定 

是安全的;基础类不可能再拥有一个比衍生类更大的接口。因此,我们通过基础类接口发送的每
返回目录 上一页 下一页 回到顶部 0 0
未阅读完?加入书签已便下次继续阅读!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!