Java编程思想第4版[中文版](PDF格式)-第75部分
按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!
而且违例名通常都是精心挑选的,可以很清楚地说明到底发生了什么事情。违例并不全是在 java。lang 中定
义的;有些是为了提供对其他库的支持,如util,net 以及io等——我们可以从它们的完整类名中看出这一
点,或者观察它们从什么继承。例如,所有IO违例都是从 java。io。IOException 继承的。
9。3。1 RuntimeException 的特殊情况
本章的第一个例子是:
if(t == null)
throw new NullPointerException();
看起来似乎在传递进入一个方法的每个句柄中都必须检查 null (因为不知道调用者是否已传递了一个有效的
句柄),这无疑是相当可怕的。但幸运的是,我们根本不必这样做——它属于 Java 进行的标准运行期检查的
一部分。若对一个空句柄发出了调用,Java 会自动产生一个NullPointerException 违例。所以上述代码在
任何情况下都是多余的。
这个类别里含有一系列违例类型。它们全部由 Java 自动生成,毋需我们亲自动手把它们包含到自己的违例规
范里。最方便的是,通过将它们置入单独一个名为 RuntimeException 的基础类下面,它们全部组合到一起。
这是一个很好的继承例子:它建立了一系列具有某种共通性的类型,都具有某些共通的特征与行为。此外,
我们没必要专门写一个违例规范,指出一个方法可能会“掷”出一个 RuntimeException,因为已经假定可能
出现那种情况。由于它们用于指出编程中的错误,所以几乎永远不必专门捕获一个“运行期违例”——
RuntimeException——它在默认情况下会自动得到处理。若必须检查 RuntimeException,我们的代码就会变
得相当繁复。在我们自己的包里,可选择“掷”出一部分 RuntimeException。
如果不捕获这些违例,又会出现什么情况呢?由于编译器并不强制违例规范捕获它们,所以假如不捕获的
话,一个RuntimeException 可能过滤掉我们到达 main()方法的所有途径。为体会此时发生的事情,请试试
下面这个例子:
//: NeverCaught。java
// Ignoring RuntimeExceptions
public class NeverCaught {
static void f() {
throw new RuntimeException(〃From f()〃);
}
static void g() {
f();
}
public static void main(String'' args) {
g();
}
270
…………………………………………………………Page 272……………………………………………………………
} ///:~
大家已经看到,一个RuntimeException (或者从它继承的任何东西)属于一种特殊情况,因为编译器不要求
为这些类型指定违例规范。
输出如下:
java。lang。RuntimeException: From f()
at NeverCaught。f(NeverCaught。java:9)
at NeverCaught。g(NeverCaught。java:12)
at NeverCaught。main(NeverCaught。java:15)
所以答案就是:假若一个RuntimeException 获得到达main()的所有途径,同时不被捕获,那么当程序退出
时,会为那个违例调用 printStackTrace()。
注意也许能在自己的代码中仅忽略 RuntimeException,因为编译器已正确实行了其他所有控制。因为
RuntimeException 在此时代表一个编程错误:
(1) 一个我们不能捕获的错误(例如,由客户程序员接收传递给自己方法的一个空句柄)。
(2) 作为一名程序员,一个应在自己的代码中检查的错误(如 ArrayIndexOutOfBoundException,此时应注
意数组的大小)。
可以看出,最好的做法是在这种情况下违例,因为它们有助于程序的调试。
另外一个有趣的地方是,我们不可将Java 违例划分为单一用途的工具。的确,它们设计用于控制那些讨厌的
运行期错误——由代码控制范围之外的其他力量产生。但是,它也特别有助于调试某些特殊类型的编程错
误,那些是编译器侦测不到的。
9。4 创建自己的违例
并不一定非要使用Java 违例。这一点必须掌握,因为经常都需要创建自己的违例,以便指出自己的库可能生
成的一个特殊错误——但创建 Java 分级结构的时候,这个错误是无法预知的。
为创建自己的违例类,必须从一个现有的违例类型继承——最好在含义上与新违例近似。继承一个违例相当
简单:
//: Inheriting。java
// Inheriting your own exceptions
class MyException extends Exception {
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
public class Inheriting {
public static vo id f() throws MyException {
System。out。println(
〃Throwing MyException from f()〃);
throw new MyException();
}
public static void g() throws MyException {
System。out。println(
〃Throwing MyException from g()〃);
throw new MyException(〃Originated in g()〃);
}
public static void main(String'' args) {
try {
271
…………………………………………………………Page 273……………………………………………………………
f();
} catch(MyException e) {
e。printStackTrace();
}
try {
g();
} catch(MyException e) {
e。printStackTrace();
}
}
} ///:~
继承在创建新类时发生:
class MyException extends Exception {
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
这里的关键是“extends Exception”,它的意思是:除包括一个Exception 的全部含义以外,还有更多的含
义。增加的代码数量非常少——实际只添加了两个构建器,对 MyException 的创建方式进行了定义。请记
住,假如我们不明确调用一个基础类构建器,编译器会自动调用基础类默认构建器。在第二个构建器中,通
过使用 super 关键字,明确调用了带有一个 String 参数的基础类构建器。
该程序输出结果如下:
Throwing MyException from f()
MyException
at Inheriting。f(Inheriting。java:16)
at Inheriting。main(Inheriting。java:24)
Throwing MyException from g()
MyException: Originated in g()
at Inheriting。g(Inheriting。java:20)
at Inheriting。main(Inheriting。java:29)
可以看到,在从f() “掷”出的MyException 违例中,缺乏详细的消息。
创建自己的违例时,还可以采取更多的操作。我们可添加额外的构建器及成员:
//: Inheriting2。java
// Inheriting your own exceptions
class MyException2 extends Exception {
public MyException2() {}
public MyException2(String msg) {
super(msg);
}
public MyException2(String msg; int x) {
super(msg);
i = x;
}
public int val() { return i; }
272
…………………………………………………………Page 274……………………………………………………………
private int i;
}
public class Inheriting2 {
public static void f() throws MyException2 {
System。out。println(
〃Throwing MyException2 from f()〃);
throw new MyException2();
}
public static void g() throws MyException2 {
System。out。println(
〃Throwing MyException2 from g()〃);
throw new MyException2(〃Originated in g()〃);
}
public static void h() throws MyException2 {
System。out。println(
〃Throwing MyException2 from h()〃);
throw new MyException2(
〃Originated in h()〃; 47);
}
public static void main(String'' args) {
try {
f();
} catch(MyException2 e) {
e。printStackTrace();
}
try {
g();
} catch(MyException2 e) {
e。printStackTrace();
}
try {
h();
} catch(MyException2 e) {
e。printStackTrace();
System。out。println(〃e。val() = 〃 + e。val());
}
}
} ///:~
此时添加了一个数据成员 i;同时添加了一个特殊的方法,用它读取那个值;也添加了一个额外的构建器,
用它设置那个值。输出结果如下:
Throwing MyException2 from f()
MyException2
at Inheriting2。f(Inheriting2。java:22)
at Inheriting2。main(Inheriting2。java:34)
Throwing MyException2 from g()
MyException2: Originated in g()
at Inheriting2。g(Inheriting2。java:26)
at Inheriting2。main(Inheriting2。java:39)
Throwing MyException2 from h()
273
…………………………………………………………Page 275……………………………………………………………
MyException2: Originated in h()
at Inheriting2。h(Inheriting2。java:30)
at Inheriting2。main(Inheriting2。java:44)
e。val() = 47
由于违例不过是另一种形式的对象,所以可以继续这个进程,进一步增强违例类的能力。但要注意,对使用
自己这个包的客户程序员来说,他们可能错过所有这些增强。因为他们可能只是简单地寻找准备生成的违
例,除此以外不做任何事情——这是大多数 Java 库违例的标准用法。若出现这种情况,有可能创建一个新违
例类型,其中几乎不包含任何代码:
//: SimpleException。java
class SimpleException extends Exception {
} ///:~
它要依赖编译器来创建默认构建器(会自动调用基础类的默认构建器)。当然,在这种情况下,我们不会得
到一个SimpleException(String)构建器,但它实际上也不会经常用到。
9。5 违例的限制
覆盖一个方法时,只能产生已在方法的基础类版本中定义的违例。这是一个重要的限制,因为它意味着与基
础类协同工作的代码也会自动应用于从基础类衍生的任何对象(当然,这属于基本的 OOP 概念),其中包括
违例。
下面这个例子演示了强加在违例身上的限制类型(在编译期):
//: StormyInning。java
// Overridden methods may throw only the
// exceptions specified in their base…class
// versions; or exceptions derived from the
// base…class exceptions。
class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}
abstract class Inning {
Inning() throws BaseballException {}
void event () throws BaseballException {
// Doesn't actually have to throw anything
}
abstract void atBat() throws Strike; Foul;
void walk() {} // Throws nothing
}
class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}
interface Storm {
void event() throws RainedOut;
void rainHard() throws RainedOut;
}
public class StormyInning extends Inning
implements Storm {
// OK to add new exceptions for constructors;
274
…………………………………………………………Page 276……………………………………………………………
// but you must deal with the base constructor
// exceptions:
StormyInning() throws Rained