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

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

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




可以指定一旦有人试图覆盖该文件就“掷”出一个违例——有的编程系统允许我们自行指定想打开一个输出 

文件,但唯一的前提是它尚不存在。但在 Java 中,似乎必须用一个File 对象来判断某个文件是否存在,因 

为假如将其作为FileOutputStream 或者FileWriter 打开,那么肯定会被覆盖。若同时指定文件和目录路 

径,File 类设计上的一个缺陷就会暴露出来,因为它会说“不要试图在单个类里做太多的事情”!  

IO流库易使我们混淆一些概念。它确实能做许多事情,而且也可以移植。但假如假如事先没有吃透装饰器方 

案的概念,那么所有的设计都多少带有一点盲目性质。所以不管学它还是教它,都要特别花一些功夫才行。 

而且它并不完整:没有提供对输出格式化的支持,而其他几乎所有语言的 IO包都提供了这方面的支持(这一 

点没有在Java 1。1 里得以纠正,它完全错失了改变库设计方案的机会,反而增添了更特殊的一些情况,使复 

杂程度进一步提高)。Java 1。1 转到那些尚未替换的 IO 库,而不是增加新库。而且库的设计人员似乎没有 

很好地指出哪些特性是不赞成的,哪些是首选的,造成库设计中经常都会出现一些令人恼火的反对消息。  

然而,一旦掌握了装饰器方案,并开始在一些较为灵活的环境使用库,就会认识到这种设计的好处。到那个 

时候,为此多付出的代码行应该不至于使你觉得太生气。  



10。11 练习  



(1) 打开一个文本文件,每次读取一行内容。将每行作为一个 String 读入,并将那个String 对象置入一个 

Vector 里。按相反的顺序打印出 Vector 中的所有行。  

(2) 修改练习 1,使读取那个文件的名字作为一个命令行参数提供。  

(3) 修改练习2,又打开一个文本文件,以便将文字写入其中。将 Vector 中的行随同行号一起写入文件。  

(4) 修改练习2,强迫Vector 中的所有行都变成大写形式,将结果发给 System。out。  

(5) 修改练习2,在文件中查找指定的单词。打印出包含了欲找单词的所有文本行。  

(6) 在Blips。java 中复制文件,将其重命名为BlipCheck。java。然后将类Blip2 重命名为BlipCheck (在进 

程中将其标记为public)。删除文件中的//!记号,并执行程序。接下来,将BlipCheck 的默认构建器变成 

注释信息。运行它,并解释为什么仍然能够工作。  

(7) 在Blip3。java 中,将接在“You must do this:”字样后的两行变成注释,然后运行程序。解释得到的 

结果为什么会与执行了那两行代码不同。  

(8) 转换SortedWordCount。java 程序,以便使用Java 1。1 IO 流。  

(9) 根据本章正文的说明修改程序 CADState。java 。  

(10) 在第 7 章(中间部分)找到GreenhouseControls。java 示例,它应该由三个文件构成。在 

GreenhouseControls。java 中,Restart()内部类有一个硬编码的事件集。请修改这个程序,使其能从一个文 

本文件里动态读取事件以及它们的相关时间。  



                                                                 332 


…………………………………………………………Page 334……………………………………………………………

                            第 11 章  运行期类型鉴定  



  

运行期类型鉴定(RTTI )的概念初看非常简单——手上只有基础类型的一个句柄时,利用它判断一个对象的 

正确类型。  

然而,对RTTI 的需要暴露出了面向对象设计许多有趣(而且经常是令人困惑的)的问题,并把程序的构造问 

题正式摆上了桌面。  

本章将讨论如何利用Java 在运行期间查找对象和类信息。这主要采取两种形式:一种是“传统”RTTI ,它假 

定我们已在编译和运行期拥有所有类型;另一种是 Java1。1 特有的“反射”机制,利用它可在运行期独立查 

找类信息。首先讨论“传统”的RTTI,再讨论反射问题。  



11。1 对 RTTI 的需要  



请考虑下面这个熟悉的类结构例子,它利用了多形性。常规类型是Shape 类,而特别衍生出来的类型是 

Circle,Square 和Triangle 。  

  



                                         

  

这是一个典型的类结构示意图,基础类位于顶部,衍生类向下延展。面向对象编程的基本目标是用大量代码 

控制基础类型(这里是 Shape)的句柄,所以假如决定添加一个新类(比如Rhomboid ,从Shape 衍生),从 

而对程序进行扩展,那么不会影响到原来的代码。在这个例子中,Shape 接口中的动态绑定方法是draw(), 

所以客户程序员要做的是通过一个普通Shape 句柄调用draw()。draw()在所有衍生类里都会被覆盖。而且由 

于它是一个动态绑定方法,所以即使通过一个普通的Shape 句柄调用它,也有表现出正确的行为。这正是多 

形性的作用。  

所以,我们一般创建一个特定的对象(Circle,Square,或者 Triangle ),把它上溯造型到一个Shape (忽 

略对象的特殊类型),以后便在程序的剩余部分使用匿名 Shape 句柄。  

作为对多形性和上溯造型的一个简要回顾,可以象下面这样为上述例子编码(若执行这个程序时出现困难, 

请参考第3 章3。1。2 小节“赋值”):  

  

//: Shapes。java  

package c11;  

import java。util。*;  

  

interface Shape {  

  void draw();  

}  

  

class Circle implements Shape {  

  public void draw() {  

    System。out。println(〃Circle。draw()〃);  

  }  

}  

  

class Square implements Shape {  



                                                                                   333 


…………………………………………………………Page 335……………………………………………………………

  public void draw() {  

    System。out。println(〃Square。draw()〃);  

  }  

}  

  

class Triangle implements Shape {  

  public void draw() {  

    System。out。println(〃Triangle。draw()〃);  

  }  

}  

  

public class Shapes {  

  public static void main(String'' args) {  

    Vector s = new Vector();  

    s。addElement(new Circle());  

    s。addElement(new Square());  

    s。addElement(new Triangle());  

    Enumeration e = s。elements();  

    while(e。hasMoreElements())  

      ((Shape)e。nextElement())。draw();  

  }  

} ///:~  

  

基础类可编码成一个 interface (接口)、一个abstract (抽象)类或者一个普通类。由于Shape 没有真正 

的成员(亦即有定义的成员),而且并不在意我们创建了一个纯粹的Shape 对象,所以最适合和最灵活的表 

达方式便是用一个接口。而且由于不必设置所有那些abstract 关键字,所以整个代码也显得更为清爽。  

每个衍生类都覆盖了基础类draw 方法,所以具有不同的行为。在main()中创建了特定类型的Shape,然后将 

其添加到一个Vector。这里正是上溯造型发生的地方,因为Vector 只容纳了对象。由于Java 中的所有东西 

 (除基本数据类型外)都是对象,所以Vector 也能容纳 Shape 对象。但在上溯造型至 Object 的过程中,任 

何特殊的信息都会丢失,其中甚至包括对象是几何形状这一事实。对Vect or 来说,它们只是Object。  

用nextElement()将一个元素从 Vector 提取出来的时候,情况变得稍微有些复杂。由于 Vector 只容纳 

Object,所以 nextElement()会自然地产生一个 Object 句柄。但我们知道它实际是个 Shape 句柄,而且希望 

将Shape 消息发给那个对象。所以需要用传统的〃(Shape)〃方式造型成一个Shape。这是RTTI 最基本的形 

式,因为在 Java 中,所有造型都会在运行期间得到检查,以确保其正确性。那正是RTTI 的意义所在:在运 

行期,对象的类型会得到鉴定。  

在目前这种情况下,RTTI 造型只实现了一部分:Object 造型成 Shape,而不是造型成Circle,Square 或者 

Triangle 。那是由于我们目前能够肯定的唯一事实就是Vector 里充斥着几何形状,而不知它们的具体类别。 

在编译期间,我们肯定的依据是我们自己的规则;而在编译期间,却是通过造型来肯定这一点。  

现在的局面会由多形性控制,而且会为Shape 调用适当的方法,以便判断句柄到底是提供Circle,Square, 

还是提供给 Triangle 。而且在一般情况下,必须保证采用多形性方案。因为我们希望自己的代码尽可能少知 

道一些与对象的具体类型有关的情况,只将注意力放在某一类对象(这里是Shape)的常规信息上。只有这 

样,我们的代码才更易实现、理解以及修改。所以说多形性是面向对象程序设计的一个常规目标。  

然而,若碰到一个特殊的程序设计问题,只有在知道常规句柄的确切类型后,才能最容易地解决这个问题, 

这个时候又该怎么办呢?举个例子来说,我们有时候想让自己的用户将某一具体类型的几何形状(如三角 

形)全都变成紫色,以便突出显示它们,并快速找出这一类型的所有形状。此时便要用到RTTI 技术,用它查 

询某个 Shape 句柄引用的准确类型是什么。  



11。1。1 Class 对象  



为理解RTTI 在 Java 里如何工作,首先必须了解类型信息在运行期是如何表示的。这时要用到一个名为 

 “Class 对象”的特殊形式的对象,其中包含了与类有关的信息(有时也把它叫作“元类”)。事实上,我 

们要用 Class 对象创建属于某个类的全部“常规”或“普通”对象。  

对于作为程序一部分的每个类,它们都有一个 Class 对象。换言之,每次写一个新类时,同时也会创建一个 



                                                                                   334 


…………………………………………………………Page 336……………………………………………………………

Class 对象(更恰当地说,是保存在一个完全同名的。class 文件中)。在运行期,一旦我们想生成那个类的 

一个对象,用于执行程序的 Java 虚拟机(JVM)首先就会检查那个类型的Class 对象是否已经载入。若尚未 

载入,JVM 就会查找同名的。class 文件,并将其载入。所以Java 程序启动时并不是完全载入的,这一点与许 

多传统语言都不同。  

一旦那个类型的Class 对象进入内存,就用它创建那一类型的所有对象。  

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