友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
Java编程思想第4版[中文版](PDF格式)-第149部分
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!
释,所以必须将接着的记号提取出来,检查它是一个正斜杠(那么这一行会被丢弃),还是一个星号。但假
如两者都不是,意味着必须在主解析循环中将刚才取出的记号送回去!幸运的是,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 search for:〃));
lower。add(searchFor);
lower。add(strip);
p。setLayout(new BorderLayout());
p。add(top; BorderLayout。NORTH);
p。add(lower; BorderLayout。SOUTH);
setLayout(new BorderLayout());
add(p; BorderLayout。NORTH);
add(results; BorderLayout。CENTER);
}
class NameL implements TextListener {
public void textValueChanged(TextEvent e) {
String nm = name。getText()。trim();
if(nm。length() == 0) {
results。setText(〃No match〃);
n = new String'0';
return;
}
try {
cl = Class。forName(nm);
} catch (ClassNotFoundException ex) {
results。setText(〃No match〃);
return;
}
640
…………………………………………………………Page 642……………………………………………………………
m = cl。getMethods();
ctor = cl。getConstructors();
// Convert to an array of Strings:
n = new String'm。length + ctor。length';
for(int i = 0 ; i 《 m。length; i++)
n'i' = m'i'。toString();
for(int i = 0; i 《 ctor。length; i++)
n'i + m。length' = ctor'i'。toString();
reDisplay();
}
}
void reDisplay() {
// Create the result set:
String'' rs = new String'n。length';
String find = searchFor。getText();
int j = 0;
// Select from the list if find exists:
for (int i = 0; i 《 n。length; i++) {
if(find == null)
rs'j++' = n'i';
else if(n'i'。indexOf(find) != …1)
rs'j++' = n'i' ;
}
results。setText(〃〃);
if(strip。getState() == true)
for (int i = 0; i 《 j; i++)
results。append(
StripQualifiers。strip(rs'i') + 〃n〃);
else // Leave qualifiers on
for (int i = 0; i 《 j; i++)
results。append(rs'i' + 〃n〃);
}
class StripL implements ItemListener {
public void itemStateChanged(ItemEvent e) {
reDisplay();
}
}
class SearchForL implements TextListener {
public void textValueChanged(TextEvent e) {
reDisplay();
}
}
public static void main(String'' args) {
DisplayMethods applet = new DisplayMethods();
Frame aFrame = new Frame(〃Display Methods〃);
aFrame。addWindowListener(
new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System。exit(0);
}
});
aFrame。add(applet; BorderLayout。CENTER);
641
…………………………………………………………Page 643……………………………………………………………
aFrame。setSize(500;750);
applet。init();
applet。start();
aFrame。setVisible(true);
}
}
class StripQualifiers {
private StreamTokenizer st;
public StripQualifiers(String qualified) {
st = new StreamTokenizer(
new StringReader(qualified));
st。ordinaryChar(' ');
}
public String getNext() {
String s = null;
try {
if(st。nextToken() !=
StreamTokenizer。TT_EOF) {
switch(st。ttype) {
case StreamTokenizer。TT_EOL:
s = null;
break;
case StreamTokenizer。TT_NUMBER:
s = Double。toString(st。nval);
break;
case StreamTokenizer。TT_WORD:
s = new String(st。sval);
break;
default: // single character in ttype
s = String。valueOf((char)st。ttype);
}
}
} catch(IOException e) {
System。out。println(e);
}
return s;
}
public static String strip(String qualified) {
StripQualifiers sq =
new StripQualifiers(qualified);
String s = 〃〃; si;
while((si = sq。getNext()) != null) {
int lastDot = si。lastIndexOf('。');
if(lastDot != …1)
si = si。substring(lastDot + 1);
s += si;
}
return s;
}
} ///:~
642
…………………………………………………………Page 644……………………………………………………………
程序中的有些东西已在以前见识过了。和本书的许多GUI 程序一样,这既可作为一个独立的应用程序使用,
亦可作为一个程序片(Applet)使用。此外,StripQualifiers 类与它在第 11 章的表现是完全一样的。
GUI 包含了一个名为name 的“文本字段”(TextField),或在其中输入想查找的类名;还包含了另一个文
本字段,名为 searchFor,可选择性地在其中输入一定的文字,希望在方法列表中查找那些文字。Checkbox
(复选框)允许我们指出最终希望在输出中使用完整的名字,还是将前面的各种限定信息删去。最后,结果
显示于一个“文本区域”(TextArea )中。
大家会注意到这个程序未使用任何按钮或其他组件,不能用它们开始一次搜索。这是由于无论文本字段还是
复选框都会受到它们的“侦听者(Listener )对象的监视。只要作出一项改变,结果列表便会立即更新。若
改变了name 字段中的文字,新的文字就会在 NameL 类中捕获。若文字不为空,则在Class。forName()中用于
尝试查找类。当然,在文字键入期间,名字可能会变得不完整,而Class。forName()会失败,这意味着它会
“掷”出一个违例。该违例会被捕获,TextArea 会随之设为“Nomatch”(没有相符)。但只要键入了一个
正确的名字(大小写也算在内),Class。forName()就会成功,而 getMethods()和 getConstructors()会分别
返回由Method 和 Constructor 对象构成的一个数组。这些数组中的每个对象都会通过toString()转变成一
个字串(这样便产生了完整的方法或构建器签名),而且两个列表都会合并到 n 中——一个独立的字串数
组。数组n 属于DisplayMethods 类的一名成员,并在调用reDisplay()时用于显示的更新。
若改变了Checkbox 或 searchFor 组件,它们的“侦听者”会简单地调用reDisplay()。reDisplay()会创建
一个临时数组,其中包含了名为rs 的字串(rs 代表“结果集”——Result Set)。结果集要么直接从n 复
制(没有find 关键字),要么选择性地从包含了 find 关键字的n 中的字串复制。最后会检查strip
Checkbox,看看用户是不是希望将名字中多余的部分删除(默认为“是”)。若答案是肯定的,则用
StripQualifiers。strip()做这件事情;反之,就将列表简单地显示出来。
在 init()中,大家也许认为在设置布局时需要进行大量繁重的工作。事实上,组件的布置完全可能只需要极
少的工作。但象这样使用BorderLayout 的好处是它允许用户改变窗口的大小,并特别能使 TextArea (文本
区域)更大一些,这意味着我们可以改变大小,以便毋需滚动即可看到更长的名字。
编程时,大家会发现特别有必要让这个工具处于运行状态,因为在试图判断要调用什么方法的时候,它提供
了最好的方法之一。
17。3 复杂性理论
下面要介绍的程序的前身是由Larry O'Brien 原创的一些代码,并以由 Craig Reynolds 于 1986 年编制的
“Boids”程序为基础,当时是为了演示复杂性理论的一个特殊问题,名为“凸显”(Emergence)。
这儿要达到的目标是通过为每种动物都规定少许简单的规则,从而逼真地再现动物的群聚行为。每个动物都
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!