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