Java——異常處理
1、java提供的異常不可能預見所有的問題,所以需要自己定義異常類,必須從已有的異常類繼承,最好選擇意思相近的異常類繼承。
class MyException extends Exception{} public class Tree1 { public static void f() throws MyException{ System.out.println("throws MyException from f()"); throw new MyException(); } public static voidmain (String[] args) { try { f(); }catch(MyException e){ System.out.println("caught it"); } } }
try塊中的代碼將被監控,catch將會接受來自try塊的異常。這裏的f()方法將會拋出一個 MyException類的異常,然後catch將會接收到這個異常,並輸出caught it4
所以輸出結果為:
throws MyException from f() caught it
可以為異常類定義一個接受字符串參數的構造器:
class MyException extends Exception{ public MyException() {} public MyException(String msg) { super(msg); } } public class Tree1 { public static void f() throws MyException{ System.out.println("throws MyException from f()"); throw new MyException(); }public static void main (String[] args) { try { f(); }catch(MyException e){ e.printStackTrace(System.out); } } }
這樣的輸出是:
throws MyException from f() MyException at Tree1.f(Tree1.java:11) at Tree1.main(Tree1.java:16)
在異常類的定義中的第二個構造器中使用了super關鍵字明確調用了其基類構造器,它接受一個字符串作為參數。
在異常處理程序中,調用了Throwable類聲明的printStackTrace()方法,就像輸出中看到的這樣,它將會打印“從方法調用處直到異常拋出處” 的方法調用序列,這裏信息發送到了System.out,如果使用默認的e.printStackTrace();則信息將被輸出到標準錯誤流。
2、Exception類型的方法
class MyException extends Exception{ public MyException() {} public MyException(String msg) { super(msg); } } class MySecondException extends Exception{ public MySecondException() {} public MySecondException(String msg) { super(msg); } } public class Tree1 { public static void f() throws MyException { System.out.println("throws MyException from f()"); throw new MyException("name"); } public static void g() throws MySecondException { System.out.println("throws MySecondException from g()"); throw new MySecondException("name2"); } public static void main (String[] args){ try { f(); // g(); }catch(Exception e){ System.out.println(e.getMessage()); System.out.println(e); System.out.println(e.getLocalizedMessage()); } } }
這裏定義了兩個繼承自Exception的異常類,分別是MyException、MySecondException,由兩個方法f()、g()拋出,如果在try塊中只有f();,輸出結果為:
throws MyException from f() name MyException: name name
Throwable類的getMessage()方法返回的是Exception的詳細消息字符串,就是拋出這個異常時候傳入的字符串,
而getLocalizedMessage()方法返回的是 Exception的本地化描述。
toString()方法返回 此對象的類的 name ": "(冒號和一個空格)
然後如果把上面代碼g();的前面的//刪掉,就是try塊中將運行f()和g()兩個方法,輸出結果為:
throws MyException from f() name MyException: name name
和剛才一樣這是因為catch接收到異常後就會結束try塊中的程序運行,所以g()方法並沒有被運行,所以如果先運行g()的結果就是:
throws MySecondException from g() name2 MySecondException: name2 name2
3、棧軌跡
public class Tree1 { public static void f(){ try { throw new Exception(); }catch(Exception e){ e.printStackTrace(); } } public static void g() {f();} public static void h() {g();} public static void main (String[] args){ f(); g(); h(); } }
輸出結果為:
java.lang.Exception at Tree1.f(Tree1.java:17) at Tree1.main(Tree1.java:27) java.lang.Exception at Tree1.f(Tree1.java:17) at Tree1.g(Tree1.java:23) at Tree1.main(Tree1.java:28) java.lang.Exception at Tree1.f(Tree1.java:17) at Tree1.g(Tree1.java:23) at Tree1.h(Tree1.java:24) at Tree1.main(Tree1.java:29)
這裏的調用的printStackTrace(),是Throwable類的方法,這個方法會將Throwable對象的棧軌跡信息打印到標準錯誤輸出流上,第一行是異常類的tostring()方法輸出的內容,後面幾行的內容都是之前通過fillInStackTrace()方法保存的內容。
java.lang.Exception at Tree1.f(Tree1.java:17) at Tree1.main(Tree1.java:27)
在這個例子中,在方法f()中拋出異常,在main方法中捕獲異常,並且打印棧軌跡信息。因此,輸出依次展示了f—>main的過程。還打印了拋出錯誤,捕獲錯誤的行數、類的名字。
java.lang.Exception at Tree1.f(Tree1.java:17) at Tree1.g(Tree1.java:23) at Tree1.main(Tree1.java:28)
這裏就是在方法f()中拋出異常,方法g()中調用了f(),然後和上一個一樣了,第三個也同樣。
public class Tree1 { public static void f(){ try { throw new Exception(); }catch(Exception e){ for(StackTraceElement ste: e.getStackTrace()) { System.out.println(ste); } } } public static void g() {f();} public static void h() {g();} public static void main (String[] args){ f(); System.out.println("------------------"); g(); System.out.println("------------------"); h(); } }
這個例子中調用了getStackTrace()方法,這個方法返回StackTraceElement類的一個對象。其輸出內容為:
Tree1.f(Tree1.java:17) Tree1.main(Tree1.java:30) ------------------ Tree1.f(Tree1.java:17) Tree1.g(Tree1.java:26) Tree1.main(Tree1.java:32) ------------------ Tree1.f(Tree1.java:17) Tree1.g(Tree1.java:26) Tree1.h(Tree1.java:27) Tree1.main(Tree1.java:34)
如果這樣使用:
public class Tree1 { public static void f(){ try { throw new Exception(); }catch(Exception e){ for(StackTraceElement ste: e.getStackTrace()) { System.out.print(ste.getMethodName() + " " ); System.out.println(ste.getLineNumber()); } } } public static void g() {f();} public static void h() {g();} public static void main (String[] args){ f(); System.out.println("------------------"); g(); System.out.println("------------------"); h(); } }
調用StackTraceElement中的方法getMethodName()和getLineNumber()就可以值獲取棧軌跡中的方法 和行:
f 17 main 30 ------------------ f 17 g 26 main 32 ------------------ f 17 g 26 h 27 main 34
所以,其實StackTraceElement是一個棧軌跡元素的數組。將這些棧軌跡元素保存在一個數組中。每個元素對應棧的一個棧幀。數組的第一個元素保存的是棧頂元素,也就是上面的f。最後一個元素保存的棧底元素,也就是main。
public class Tree1 { public static void f() throws Exception{ throw new Exception(); } public static void g() throws Exception { try { f(); }catch(Exception e){ e.printStackTrace(); throw e; } } public static void main (String[] args){ try { g(); }catch(Exception e){ e.printStackTrace(); } } }
4、重新拋出異常
這裏f()函數中拋出一個異常,然後g()方法中捕獲了這個異常並打印異常棧軌跡Stack Trace,然後又再拋出了剛剛的異常,mai方法中捕獲 了這個異常並打印異常棧軌跡Stack Trace,所以結果為:
java.lang.Exception at Tree1.f(Tree1.java:16) at Tree1.g(Tree1.java:21) at Tree1.main(Tree1.java:30) java.lang.Exception at Tree1.f(Tree1.java:16) at Tree1.g(Tree1.java:21) at Tree1.main(Tree1.java:30)
也就是說,捕獲到異常又立即拋出,在上級方法調用中再次捕獲這個異常,打印的棧軌跡信息是一樣的。額,其實只看代碼也知道,因為這裏只是把剛剛異常e又拋出一次,
public class Tree1 { public static void f() throws Exception{ throw new Exception(); } public static void g() throws Exception { try { f(); }catch(Exception e){ e.printStackTrace(); throw (Exception)e.fillInStackTrace(); } } public static void main (String[] args){ try { g(); }catch(Exception e){ e.printStackTrace(); } } }
throw (Exception)e.fillInStackTrace();
這一行調用了Exception的fillInStackTrace()方法,首先要知道其實這個方法並不是來自於Exception類。Exception類本身除了定義了幾個構造器之外,所有的方法都是從其父類繼承過來的。而和異常相關的方法都是從java.lang.Throwable類繼承過來的,所以其實fillInStackTrace()是Throwable類的方法,然後fillInStackTrace()方法就是將當前線程當前狀態下的軌跡棧的狀態保存進Throwabe中,就是更新的意思。
public Throwable fillInStackTrace()
這是這個方法的原型所以它的返回值是一個Throwable類對象,所以使用它更新的時候需要強制類型轉換。
throw (Exception)e.fillInStackTrace();
所以輸出內容為:
java.lang.Exception at Tree1.f(Tree1.java:16) at Tree1.g(Tree1.java:21) at Tree1.main(Tree1.java:30) java.lang.Exception at Tree1.g(Tree1.java:24) at Tree1.main(Tree1.java:30)
這次第二個打印的棧軌跡信息就沒有f了。
5、異常鏈
class MyException1 extends Exception{} class MyException2 extends Exception{} public class Tree1 { public static void f() throws MyException1{ throw new MyException1(); } public static void g() throws MyException2{ try { f(); } catch (MyException1 e) { e.printStackTrace(); throw new MyException2(); } } public static void main (String[] args){ try { g(); }catch(Exception e){ e.printStackTrace(); } } }
這裏定義了兩個異常類,f()方法拋出了MyException1,g()方法捕獲了這個異常並打印了異常的棧軌鏈,然後拋出了另一個異常MyException2也打印了異常的棧軌鏈,所以輸出結果為:
MyException1 at Tree1.f(Tree1.java:16) at Tree1.g(Tree1.java:21) at Tree1.main(Tree1.java:31) MyException2 at Tree1.g(Tree1.java:24) at Tree1.main(Tree1.java:31)
這沒什麽毛病,但是常常想要在捕獲一個異常後拋出另一個異常,並且希望把原始異常的信息保存下來,這就需要將原始異常的信息包裝在新的異常中,這被稱為異常鏈
class MyException1 extends Exception{} class MyException2 extends Exception{ MyException2(Throwable throwable){ super(throwable); } MyException2(){ super(); } } public class Tree1 { public static void f() throws MyException1{ throw new MyException1(); } public static void g() throws MyException2{ try { f(); } catch (MyException1 e) { e.printStackTrace(); throw new MyException2(e); } } public static void main (String[] args){ try { g(); }catch(Exception e){ e.printStackTrace(); } } }
這樣的輸出結果就是:
MyException1 at Tree1.f(Tree1.java:16) at Tree1.g(Tree1.java:21) at Tree1.main(Tree1.java:31) MyException2: MyException1 at Tree1.g(Tree1.java:24) at Tree1.main(Tree1.java:31) Caused by: MyException1 at Tree1.f(Tree1.java:16) at Tree1.g(Tree1.java:21) ... 1 more
這樣的定義:
public class MyException2 extends Exception{ //定義異常的原因 public MyException2(String message){ super(message); } //定義異常原因,並攜帶原始的異常 public MyException2(String message,Throwable cause){ super(message,cause); } //保留原始異常信息 publicMyException2(Throwable cause){ super(cause); } }
就能將原始異常傳遞給新異常
或是:
class MyException1 extends Exception{ } class MyException2 extends Exception{ MyException2(Throwable throwable){ super(throwable); } MyException2(){ super(); } } class MyException3 extends Exception{ MyException3(Throwable throwable){ super(throwable); } MyException3(){ super(); } } public class Tree1 { public static void f() throws MyException1{ throw new MyException1(); } public static void g() throws MyException2{ try { f(); } catch (MyException1 e) { // e.printStackTrace(); throw new MyException2(e); } } public static void h() throws MyException3{ try { g(); } catch (MyException2 e) { // e.printStackTrace(); throw new MyException3(e); } } public static void main (String[] args){ try { h(); }catch(Exception e){ e.printStackTrace(); } } }
這裏值輸出了最後一個異常的棧軌跡
MyException3: MyException2: MyException1 at Tree1.h(Tree1.java:44) at Tree1.main(Tree1.java:51) Caused by: MyException2: MyException1 at Tree1.g(Tree1.java:35) at Tree1.h(Tree1.java:41) ... 1 more Caused by: MyException1 at Tree1.f(Tree1.java:27) at Tree1.g(Tree1.java:32) ... 2 more
其中還包括了MyException1和MyException2 的信息
Exception
Java——異常處理