並不一定非要使用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 void 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 {
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; }
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()
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)構建器,但它實際上也不會經常用到。