按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
⑥:在 C++里,“破坏器”可帮我们控制这一局面。
282
…………………………………………………………Page 284……………………………………………………………
在Cleanup。java 中,我们创建了一个InputFile,用它打开用于创建程序的相同的源文件。同时一次读取该
文件的一行内容,而且添加相应的行号。所有违例都会在 main()中被捕获——尽管我们可选择更大的可靠
性。
这个示例也向大家展示了为何在本书的这个地方引入违例的概念。违例与 Java 的编程具有很高的集成度,这
主要是由于编译器会强制它们。只有知道了如何操作那些违例,才可更进一步地掌握编译器的知识。
9。8 违例匹配
“掷”出一个违例后,违例控制系统会按当初编写的顺序搜索“最接近”的控制器。一旦找到相符的控制
器,就认为违例已得到控制,不再进行更多的搜索工作。
在违例和它的控制器之间,并不需要非常精确的匹配。一个衍生类对象可与基础类的一个控制器相配,如下
例所示:
//: Human。java
// Catching Exception Hierarchies
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
public class Human {
public static void main(String '' args) {
try {
throw new Sneeze();
} catch(Sneeze s) {
System。out。println(〃Caught Sneeze〃);
} catch(Annoyance a) {
System。out。println(〃Caught Annoyance〃);
}
}
} ///:~
Sneeze 违例会被相符的第一个 catch 从句捕获。当然,这只是第一个。然而,假如我们删除第一个 catch 从
句:
try {
throw new Sneeze();
} catch(Annoyance a) {
System。out。println(〃Caught Annoyance〃);
}
那么剩下的 catch 从句依然能够工作,因为它捕获的是 Sneeze 的基础类。换言之,catch(Annoyance e)能捕
获一个Annoyance 以及从它衍生的任何类。这一点非常重要,因为一旦我们决定为一个方法添加更多的违
例,而且它们都是从相同的基础类继承的,那么客户程序员的代码就不需要更改。至少能够假定它们捕获的
是基础类。
若将基础类捕获从句置于第一位,试图“屏蔽”衍生类违例,就象下面这样:
try {
throw new Sneeze();
} catch(Annoyance a) {
System。out。println(〃Caught Annoyance〃);
} catch(Sneeze s) {
System。out。println(〃Caught Sneeze〃);
283
…………………………………………………………Page 285……………………………………………………………
}
则编译器会产生一条出错消息,因为它发现永远不可能抵达Sneeze 捕获从句。
9。8。1 违例准则
用违例做下面这些事情:
(1) 解决问题并再次调用造成违例的方法。
(2) 平息事态的发展,并在不重新尝试方法的前提下继续。
(3) 计算另一些结果,而不是希望方法产生的结果。
(4) 在当前环境中尽可能解决问题,以及将相同的违例重新“掷”出一个更高级的环境。
(5) 在当前环境中尽可能解决问题,以及将不同的违例重新“掷”出一个更高级的环境。
(6) 中止程序执行。
(7) 简化编码。若违例方案使事情变得更加复杂,那就会令人非常烦恼,不如不用。
(8) 使自己的库和程序变得更加安全。这既是一种“短期投资”(便于调试),也是一种“长期投资”(改
善应用程序的健壮性)
9。9 总结
通过先进的错误纠正与恢复机制,我们可以有效地增强代码的健壮程度。对我们编写的每个程序来说,错误
恢复都属于一个基本的考虑目标。它在Java 中显得尤为重要,因为该语言的一个目标就是创建不同的程序组
件,以便其他用户(客户程序员)使用。为构建一套健壮的系统,每个组件都必须非常健壮。
在Java 里,违例控制的目的是使用尽可能精简的代码创建大型、可靠的应用程序,同时排除程序里那些不能
控制的错误。
违例的概念很难掌握。但只有很好地运用它,才可使自己的项目立即获得显著的收益。Java 强迫遵守违例所
有方面的问题,所以无论库设计者还是客户程序员,都能够连续一致地使用它。
9。10 练习
(1) 用main()创建一个类,令其掷出 try 块内的 Exception 类的一个对象。为 Exception 的构建器赋予一个
字串参数。在catch 从句内捕获违例,并打印出字串参数。添加一个finally 从句,并打印一条消息,证明
自己真正到达那里。
(2) 用 extends 关键字创建自己的违例类。为这个类写一个构建器,令其采用String 参数,并随同 String
句柄把它保存到对象内。写一个方法,令其打印出保存下来的 String。创建一个 try…catch 从句,练习实际
操作新违例。
(3) 写一个类,并令一个方法掷出在练习2 中创建的类型的一个违例。试着在没有违例规范的前提下编译
它,观察编译器会报告什么。接着添加适当的违例规范。在一个try…catch 从句中尝试自己的类以及它的违
例。
(4) 在第 5 章,找到调用了Assert。java 的两个程序,并修改它们,令其掷出自己的违例类型,而不是打印
到System。err。该违例应是扩展了RuntimeException 的一个内部类。
284
…………………………………………………………Page 286……………………………………………………………
第 10 章 Java IO 系统
“对语言设计人员来说,创建好的输入/输出系统是一项特别困难的任务。”
由于存在大量不同的设计方案,所以该任务的困难性是很容易证明的。其中最大的挑战似乎是如何覆盖所有
可能的因素。不仅有三种不同的种类的 IO 需要考虑(文件、控制台、网络连接),而且需要通过大量不同的
方式与它们通信(顺序、随机访问、二进制、字符、按行、按字等等)。
Java 库的设计者通过创建大量类来攻克这个难题。事实上,Java 的 IO系统采用了如此多的类,以致刚开始
会产生不知从何处入手的感觉(具有讽刺意味的是,Java 的 IO设计初衷实际要求避免过多的类)。从Java
1。0升级到 Java 1。1 后,IO 库的设计也发生了显著的变化。此时并非简单地用新库替换旧库,Sun 的设计人
员对原来的库进行了大手笔的扩展,添加了大量新的内容。因此,我们有时不得不混合使用新库与旧库,产
生令人无奈的复杂代码。
本章将帮助大家理解标准Java 库内的各种 IO 类,并学习如何使用它们。本章的第一部分将介绍“旧”的
Java 1。0 IO 流库,因为现在有大量代码仍在使用那个库。本章剩下的部分将为大家引入Java 1。1 IO 库的
一些新特性。注意若用 Java 1。1 编译器来编译本章第一部分介绍的部分代码,可能会得到一条“不建议使用
该特性”(Deprecated feature )警告消息。代码仍然能够使用;编译器只是建议我们换用本章后面要讲述
的一些新特性。但我们这样做是有价值的,因为可以更清楚地认识老方法与新方法之间的一些差异,从而加
深我们的理解(并可顺利阅读为Java 1。0 写的代码)。
10。1 输入和输出
可将Java 库的 IO类分割为输入与输出两个部分,这一点在用 Web 浏览器阅读联机Java 类文档时便可知道。
通过继承,从 InputStream (输入流)衍生的所有类都拥有名为read()的基本方法,用于读取单个字节或者
字节数组。类似地,从 OutputStream 衍生的所有类都拥有基本方法 write(),用于写入单个字节或者字节数
组。然而,我们通常不会用到这些方法;它们之所以存在,是因为更复杂的类可以利用它们,以便提供一个
更有用的接口。因此,我们很少用单个类创建自己的系统对象。一般情况下,我们都是将多个对象重叠在一
起,提供自己期望的功能。我们之所以感到Java 的流库(Stream Library)异常复杂,正是由于为了创建单
独一个结果流,却需要创建多个对象的缘故。
很有必要按照功能对类进行分类。库的设计者首先决定与输入有关的所有类都从 InputStream 继承,而与输
出有关的所有类都从OutputStream 继承。
10。1。1 InputStream 的类型
InputStream 的作用是标志那些从不同起源地产生输入的类。这些起源地包括(每个都有一个相关的
InputStream子类):
(1) 字节数组
(2) String对象
(3) 文件
(4) “管道”,它的工作原理与现实生活中的管道类似:将一些东西置入一端,它们在另一端出来。 (5) 一
系列其他流,以便我们将其统一收集到单独一个流内。
(6) 其他起源地,如 Internet 连接等(将在本书后面的部分讲述)。
除此以外,FilterInputStream 也属于 InputStream 的一种类型,用它可为“破坏器”类提供一个基础类,
以便将属性或者有用的接口同输入流连接到一起。这将在以后讨论。
表 10。1 InputStream 的类型
类 功能 构建器参数/如何使用
ByteArrayInputStream 允许内存中的一个缓冲区作为 InputStream使用 从中提取字节的缓冲区/作为一个
数据源使用。通过将其同一个 FilterInputStream 对象连接,可提供一个有用的接口
285
…………………………………………………………Page 287……………………………………………………………
StringBufferInputStream 将一个 String 转换成 InputStream 一个String (字串)。基础的实施方案实际
采用一个StringBuffer (字串缓冲)/作为一个数据源使用。通过将其同一个FilterInputStream 对象连
接,可提供一个有用的接口
FileInputStream 用于从文件读取信息 代表文件名的一个 String,或者一个 File 或FileDescriptor 对象
/作为一个数据源使用。通过将其同一个FilterInp