Java编程思想第4版[中文版](PDF格式)-第83部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
analyze(〃Are you sad about this?〃);
analyze(〃It's you! I am happy〃);
analyze(〃It's you! I am sad〃);
303
…………………………………………………………Page 305……………………………………………………………
}
static StringTokenizer st;
static void analyze(String s) {
prt(〃nnew sentence 》》 〃 + s);
boolean sad = false;
st = new StringTokenizer(s);
while (st。hasMoreTokens()) {
String token = next();
// Look until you find one of the
// two starting tokens:
if(!token。equals(〃I〃) &&
!token。equals(〃Are〃))
continue; // Top of while loop
if(token。equals(〃I〃)) {
String tk2 = next();
if(!tk2。equals(〃am〃)) // Must be after I
break; // Out of while loop
else {
String tk3 = next();
if(tk3。equals(〃sad〃)) {
sad = true;
break; // Out of while loop
}
if (tk3。equals(〃not〃)) {
String tk4 = next();
if(tk4。equals(〃sad〃))
break; // Leave sad false
if(tk4。equals(〃happy〃)) {
sad = true;
break;
}
}
}
}
if(token。equals(〃Are〃)) {
String tk2 = next();
if(!tk2。equals(〃you〃))
break; // Must be after Are
String tk3 = next();
if(tk3。equals(〃sad〃))
sad = true;
break; // Out of while loop
}
}
if(sad) prt(〃Sad detected〃);
}
static String next() {
if(st。hasMoreTokens()) {
String s = st。nextToken();
prt(s);
return s;
}
304
…………………………………………………………Page 306……………………………………………………………
else
return 〃〃;
}
static void prt(String s) {
System。out。println(s);
}
} ///:~
对于准备分析的每个字串,我们进入一个while 循环,并将记号从那个字串中取出。请注意第一个 if 语句,
假如记号既不是“I”,也不是“Are”,就会执行continue (返回循环起点,再一次开始)。这意味着除非
发现一个“I”或者“Are”,才会真正得到记号。大家可能想用==代替 equals()方法,但那样做会出现不正
常的表现,因为==比较的是句柄值,而equals() 比较的是内容。
analyze()方法剩余部分的逻辑是搜索“I am sad”(我很忧伤、“I am nothappy”(我不快乐)或者“Are
you sad? ”(你悲伤吗?)这样的句法格式。若没有break 语句,这方面的代码甚至可能更加散乱。大家应
注意对一个典型的解析器来说,通常都有这些记号的一个表格,并能在读取新记号的时候用一小段代码在表
格内移动。
无论如何,只应将 StringTokenizer 看作StreamTokenizer 一种简单而且特殊的简化形式。然而,如果有一
个字串需要进行记号处理,而且StringTokenizer 的功能实在有限,那么应该做的全部事情就是用
StringBufferInputStream 将其转换到一个数据流里,再用它创建一个功能更强大的StreamTokenizer。
10。7 Java 1。1 的 IO 流
到这个时候,大家或许会陷入一种困境之中,怀疑是否存在IO 流的另一种设计方案,并可能要求更大的代码
量。还有人能提出一种更古怪的设计吗?事实上,Java 1。1 对 IO 流库进行了一些重大的改进。看到 Reader
和Writer 类时,大多数人的第一个印象(就象我一样)就是它们用来替换原来的 InputStream 和
OutputStream 类。但实情并非如此。尽管不建议使用原始数据流库的某些功能(如使用它们,会从编译器收
到一条警告消息),但原来的数据流依然得到了保留,以便维持向后兼容,而且:
(1) 在老式层次结构里加入了新类,所以Sun 公司明显不会放弃老式数据流。
(2) 在许多情况下,我们需要与新结构中的类联合使用老结构中的类。为达到这个目的,需要使用一些
“桥”类:InputStreamReader将一个 InputStream转换成 Reader,OutputStreamWriter 将一个
OutputStream 转换成 Writer。
所以与原来的 IO 流库相比,经常都要对新 IO 流进行层次更多的封装。同样地,这也属于装饰器方案的一个
缺点——需要为额外的灵活性付出代价。
之所以在Java 1。1 里添加了Reader 和 Writer 层次,最重要的原因便是国际化的需求。老式 IO流层次结构
只支持8 位字节流,不能很好地控制 16 位Unicode 字符。由于Unicode 主要面向的是国际化支持(Java 内
含的 char 是 16 位的Unicode),所以添加了Reader 和 Writer 层次,以提供对所有 IO 操作中的Unicode 的
支持。除此之外,新库也对速度进行了优化,可比旧库更快地运行。
与本书其他地方一样,我会试着提供对类的一个概述,但假定你会利用联机文档搞定所有的细节,比如方法
的详尽列表等。
10。7。1 数据的发起与接收
Java 1。0 的几乎所有 IO流类都有对应的Java 1。1 类,用于提供内建的Unicode 管理。似乎最容易的事情就
是“全部使用新类,再也不要用旧的”,但实际情况并没有这么简单。有些时候,由于受到库设计的一些限
制,我们不得不使用Java 1。0 的 IO流类。特别要指出的是,在旧流库的基础上新加了 java。util。zip 库,
它们依赖旧的流组件。所以最明智的做法是“尝试性”地使用 Reader 和 Writer 类。若代码不能通过编译,
便知道必须换回老式库。
下面这张表格分旧库与新库分别总结了信息发起与接收之间的对应关系。
发起&接收:Java 1。0 类 对应的 Java 1。1 类
Sources & Sinks: Corresponding Java 1。1 class
Java 1。0 class
305
…………………………………………………………Page 307……………………………………………………………
I n p u t S t r e a m R e a d e r
converter: InputStreamReader
O u t p u t S t r e a m W r i t e r
converter: OutputStreamWriter
F i l e I n p u t S t r e a m F i l e R e a d e r
F i l e O u t p u t S t r e a m F i l e W r i t e r
StringBufferInputStream S t r i n g R e a d e r
(no corresponding class) S t r i n g W r i t e r
ByteArrayInputStream C h a r A r r a y R e a d e r
ByteArrayOutputStream C h a r A r r a y W r i t e r
P i p e d I n p u t S t r e a m P i p e d R e a d e r
P i p e d O u t p u t S t r e a m P i p e d W r i t e r
我们发现即使不完全一致,但旧库组件中的接口与新接口通常也是类似的。
10。7。2 修改数据流的行为
在Java 1。0 中,数据流通过FilterInputStream 和FilterOutputStream 的“装饰器”(Decorator)子类适
应特定的需求。Java 1。1 的 IO流沿用了这一思想,但没有继续采用所有装饰器都从相同“filter”(过滤
器)基础类中衍生这一做法。若通过观察类的层次结构来理解它,这可能令人出现少许的困惑。
在下面这张表格中,对应关系比上一张表要粗糙一些。之所以会出现这个差别,是由类的组织造成的:尽管
BufferedOutputStream 是 FilterOutputStream 的一个子类,但是BufferedWriter 并不是FilterWriter 的
子类(对后者来说,尽管它是一个抽象类,但没有自己的子类或者近似子类的东西,也没有一个“占位符”
可用,所以不必费心地寻找)。然而,两个类的接口是非常相似的,而且不管在什么情况下,显然应该尽可
能地使用新版本,而不应考虑旧版本(也就是说,除非在一些类中必须生成一个Stream,不可生成 Reader
或者Writer)。
过滤器:Java 1。0 类 对应的 Java 1。1 类
FilterInputStream FilterReader
FilterOutputStream FilterWriter (没有子类的抽象类)
BufferedInputStream BufferedReader (也有readLine())
BufferedOutputStream BufferedWriter
DataInputStream 使用DataInputStream (除非要使用readLine(),那时需要使用一个BufferedReader)
PrintStream PrintWriter
LineNumberInputStream LineNumberReader
StreamTokenizer StreamTokenizer (用构建器取代Reader)
PushBackInputStream PushBackReader
有一条规律是显然的:若想使用readLine(),就不要再用一个DataInputStream 来实现(否则会在编译期得
到一条出错消息),而应使用一个 BufferedReader。但除这种情况以外,DataInputStream 仍是Java 1。1
IO库的“首选”成员。
为了将向PrintWriter 的过渡变得更加自然,它提供了能采用任何OutputStream 对象的构建器。
PrintWriter 提供的格式化支持没有 PrintStream 那么多;但接口几乎是相同的。
10。7。3 未改变的类
显然,Java 库的设计人员觉得以前的一些类毫无问题,所以没有对它们作任何修改,可象以前那样继续使用
它们:
没有对应Java 1。1 类的Java 1。0 类
306
…………………………………………………………Page 308……………………………………………………………
DataOutputStream
File
RandomAccessFile
SequenceInputStream
特别未加改动的是 DataOutputStream,所以为了用一种可转移的格式保存和获取数据,必须沿用
InputStream和 OutputStream 层次结构。
10。7。4 一个例子
为体验新类的效果,下面让我们看看如何修改 IOStreamDemo。java示例的相应区域,以便使用Reader 和
Writer 类:
//: NewIODemo。java
// Java 1。1 IO typical usage
import java。io。*;
public class NewIODemo {
public static void main(String'' args) {
try {
// 1。 Reading input by lines:
BufferedReader in =
new BufferedReader(
new FileReader(args'0'));
String s; s2 = new String();
while((s = in。readLine())!= null)
s2 += s + 〃n〃;
in。close();
// 1b。 Reading standard input:
BufferedReader stdin =
new BufferedReader(
new InputStreamReader(System。in));
System。out。print(〃Enter a line:〃);
System。out。println(stdin。readLine());
// 2。 Input from memory
StringReader in2 = new StringReader(s2);
int c;
while((c = in2。read()) != …1)
System。out。print((char)c);
// 3。 Formatted memory input
try {
DataInputStream in3 =
new DataInputStream(
// Oops: must use deprecated class:
new StringBufferInputStream(s2));
while(true)
System。out。print((char)in3。readByte());
} catch(EOFException e) {
System。out。println(〃End of stream〃);
307
…………………………………………………………Page 309……………………………………………………………
}
// 4。 Line numbering & file output
try {
LineNumberReader li =
new