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

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

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




        BufferedOutputStream out =  

          new BufferedOutputStream(  

            new FileOutputStream(args'0'));  

        c。classes。save(out;  

          〃Classes found by ClassScanner。java〃);  

        out。close();  

      } catch(IOException e) {  

        System。err。println(  

          〃Could not write 〃 + args'0');  

        System。exit(1);  

      }  

    }  

  }  

}  

  

class JavaFilter implements FilenameFilter {  

  public boolean accept(File dir; String name) {  

    // Strip path information:  

    String f = new File(name)。getName();  

    return f。trim()。endsWith(〃。java〃);  

  }  

} ///:~  

  

MultiStringMap 类是个特殊的工具,允许我们将一组字串与每个键项对应(映射)起来。和前例一样,这里 

也使用了一个散列表(Hashtable),不过这次设置了继承。该散列表将键作为映射成为Vector 值的单一的 

字串对待。add()方法的作用很简单,负责检查散列表里是否存在一个键。如果不存在,就在其中放置一个。 

getVector()方法为一个特定的键产生一个 Vector;而printValues()将所有值逐个Vector 地打印出来,这 

对程序的调试非常有用。  

为简化程序,来自标准 Java 库的类名全都置入一个 Properties (属性)对象中(来自标准Java 库)。记住 

Properties 对象实际是个散列表,其中只容纳了用于键和值项的 String 对象。然而仅需一次方法调用,我 

们即可把它保存到磁盘,或者从磁盘中恢复。实际上,我们只需要一个名字列表,所以为键和值都使用了相 

同的对象。  

针对特定目录中的文件,为找出相应的类与标识符,我们使用了两个MultiStringMap:classMap 以及 

identMap。此外在程序启动的时候,它会将标准类名仓库装载到名为classes 的Properties 对象中。一旦在 

本地目录发现了一个新类名,也会将其加入classes 以及classMap 。这样一来,classMap 就可用于在本地目 

录的所有类间遍历,而且可用 classes 检查当前标记是不是一个类名(它标记着对象或方法定义的开始,所 

以收集接下去的记号——直到碰到一个分号——并将它们都置入 identMap )。  

ClassScanner 的默认构建器会创建一个由文件名构成的列表(采用FilenameFilter 的JavaFilter 实现形 

式,参见第 10章)。随后会为每个文件名都调用 scanListing()。  

在 scanListing() 内部,会打开源码文件,并将其转换成一个StreamTokenizer。根据Java 帮助文档,将 

true 传递给 slashStartments()和 slashSlashments()的本意应当是剥除那些注释内容,但这样做似 

乎有些问题(在Java 1。0 中几乎无效)。所以相反,那些行被当作注释标记出去,并用另一个方法来提取注 

释。为达到这个目的,'/'必须作为一个原始字符捕获,而不是让StreamTokeinzer 将其当作注释的一部分对 

待。此时要用ordinaryChar()方法指示StreamTokenizer 采取正确的操作。同样的道理也适用于点号 

 ('。'),因为我们希望让方法调用分离出单独的标识符。但对下划线来说,它最初是被StreamTokenizer 当 

作一个单独的字符对待的,但此时应把它留作标识符的一部分,因为它在 static final值中以 TT_EOF 等等 

形式使用。当然,这一点只对目前这个特殊的程序成立。wordChars()方法需要取得我们想添加的一系列字 

符,把它们留在作为一个单词看待的记号中。最后,在解析单行注释或者放弃一行的时候,我们需要知道一 



                                                                                       638 


…………………………………………………………Page 640……………………………………………………………

个换行动作什么时候发生。所以通过调用 eollsSignificant(true) ,换行符(EOL)会被显示出来,而不是 

被StreamTokenizer 吸收。  

scanListing()剩余的部分将读入和检查记号,直至文件尾。一旦 nextToken()返回一个 final static值— 

—StreamTokenizer。TT_EOF ,就标志着已经抵达文件尾部。  

若记号是个'/',意味着它可能是个注释,所以就调用eatments(),对这种情况进行处理。我们在这儿唯 

一感兴趣的其他情况是它是否为一个单词,当然还可能存在另一些特殊情况。  

如果单词是 class (类)或interface (接口),那么接着的记号就应当代表一个类或接口名字,并将其置入 

classes和 classMap。若单词是 import或者package,那么我们对这一行剩下的东西就没什么兴趣了。其他 

所有东西肯定是一个标识符(这是我们感兴趣的),或者是一个关键字(对此不感兴趣,但它们采用的肯定 

是小写形式,所以不必兴师动众地检查它们)。它们将加入到 identMap。  

discardLine()方法是一个简单的工具,用于查找行末位置。注意每次得到一个新记号时,都必须检查行末。  

只要在主解析循环中碰到一个正斜杠,就会调用eatments()方法。然而,这并不表示肯定遇到了一条注 

释,所以必须将接着的记号提取出来,检查它是一个正斜杠(那么这一行会被丢弃),还是一个星号。但假 

如两者都不是,意味着必须在主解析循环中将刚才取出的记号送回去!幸运的是,pushBack()方法允许我们 

将当前记号“压回”输入数据流。所以在主解析循环调用 nextToken()的时候,它能正确地得到刚才送回的 

东西。  

为方便起见,classNames()方法产生了一个数组,其中包含了 classes集合中的所有名字。这个方法未在程 

序中使用,但对代码的调试非常有用。  

接下来的两个方法是实际进行检查的地方。在 checkClassNames()中,类名从 classMap 提取出来(请记住, 

classMap 只包含了这个目录内的名字,它们按文件名组织,所以文件名可能伴随错误的类名打印出来)。为 

做到这一点,需要取出每个关联的 Vector,并遍历其中,检查第一个字符是否为小写。若确实为小写,则打 

印出相应的出错提示消息。  

在 checkIdentNames()中,我们采用了一种类似的方法:每个标识符名字都从identMap 中提取出来。如果名 

字不在 classes 列表中,就认为它是一个标识符或者关键字。此时会检查一种特殊情况:如果标识符的长度 

等于3 或者更长,而且所有字符都是大写的,则忽略此标识符,因为它可能是一个static final值,比如 

TT_EOF。当然,这并不是一种完美的算法,但它假定我们最终会注意到任何全大写标识符都是不合适的。  

这个方法并不是报告每一个以大写字符开头的标识符,而是跟踪那些已在一个名为reportSet()的Vector 中 

报告过的。它将Vector 当作一个“集合”对待,告诉我们一个项目是否已在那个集合中。该项目是通过将文 

件名和标识符连接起来生成的。若元素不在集合中,就加入它,然后产生报告。  

程序列表剩下的部分由 main()构成,它负责控制命令行参数,并判断我们是准备在标准 Java 库的基础上构 

建由一系列类名构成的“仓库”,还是想检查已写好的那些代码的正确性。不管在哪种情况下,都会创建一 

个ClassScanner 对象。  

无论准备构建一个“仓库”,还是准备使用一个现成的,都必须尝试打开现有仓库。通过创建一个File 对象 

并测试是否存在,就可决定是否打开文件并在 ClassScanner 中装载 classes 这个Properties 列表(使用 

load())。来自仓库的类将追加到由ClassScanner 构建器发现的类后面,而不是将其覆盖。如果仅提供一个 

命令行参数,就意味着自己想对类名和标识符名字进行一次检查。但假如提供两个参数(第二个是〃…a〃), 

就表明自己想构成一个类名仓库。在这种情况下,需要打开一个输出文件,并用 Properties。save()方法将 

列表写入一个文件,同时用一个字串提供文件头信息。  



17。2 方法查找工具  



第 11 章介绍了Java 1。1 新的“反射”概念,并利用这个概念查询一个特定类的方法——要么是由所有方法 

构成的一个完整列表,要么是这个列表的一个子集(名字与我们指定的关键字相符)。那个例子最大的好处 

就是能自动显示出所有方法,不强迫我们在继承结构中遍历,检查每一级的基础类。所以,它实际是我们节 

省编程时间的一个有效工具:因为大多数 Java 方法的名字都规定得非常全面和详尽,所以能有效地找出那些 

包含了一个特殊关键字的方法名。若找到符合标准的一个名字,便可根据它直接查阅联机帮助文档。  

但第 11 的那个例子也有缺陷,它没有使用AWT,仅是一个纯命令行的应用。在这儿,我们准备制作一个改进 

的GUI 版本,能在我们键入字符的时候自动刷新输出,也允许我们在输出结果中进行剪切和粘贴操作:  

  

//: DisplayMethods。java  

// Display the methods of any class inside  

// a window。 Dynamically narrows your search。  

import java。awt。*;  



                                                                   639 


…………………………………………………………Page 641……………………………………………………………

import java。awt。event。*;  

import java。applet。*;  

import java。lang。reflect。*;  

import java。io。*;  

  

public class DisplayMethods extends Applet {  

  Class cl;  

  Method'' m;  

  Constructor'' ctor;  

  String'' n = new String'0';  

  TextField   

    name = new TextField(40);  

    searchFor = new TextField(30);  

  Checkbox strip =   

    new Checkbox(〃Strip Qualifiers〃);  

  TextArea results = new TextArea(40; 65);  

  public void init() {  

    strip。setState(true);  

    name。addTextListener(new NameL());  

    searchFor。addTextListener(new SearchForL());  

    strip。addItemListener(new StripL());  

    Panel   

      top = new Panel();  

      lower = new Panel();  

      p = new Panel();  

    top。add(new Label(〃Qualified class name:〃));  

    top。add(name);  

    lower。add(  

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