Thinking in java系列博文目錄:java
本篇文章將講述關於異常的相關知識程序員
注: 本文首發於 My 公衆號 CodeSheep ,可 長按 或 掃描 下面的 當心心 來訂閱 ↓ ↓ ↓編程
if( t==null ) throw new NullPointerException(); // 異常對象用new建立於堆上
try { ... } catch( Type1 id1 ) { // 處理Type1類型的異常代碼 } catch( Type2 id2 ) { // 處理Type2類型的異常代碼 }
建立不帶參數ctor的自定義異常類:安全
// 自定義異常類(default ctor) class SimpleException extends Exception {} ------------------------------------------------------------ // 客戶端代碼 public class UseException { public void fun throws SimpleException { System.out.println( "Throw SimpleExcetion from fun" ); throw new SimpleException(); } public static void main( String[] args ) { UseException user = new UseException(); try { user.fun(); } catch( SimpleException e ) { System.out.println("Caught it !"); } } } ------------------------------------------------------------ // 輸出 Throw SimpleExcetion from fun Caught it !
建立帶參數ctor的自定義異常類網絡
// 自定義異常類(有參ctor) class MyException extends Exception { public MyException() { } public MyException( String msg ) { super(msg); } } ------------------------------------------------------------ // 客戶端代碼 public class UseException { pubilc 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()"); } publib static void main( String[] args ) { try { f(); } catch( MyException e ) { e.printStackTrace( System.out ); } try { g(); } catch( MyException e ) { e.printStackTrace( System.out ); } } } ------------------------------------------------------------ // 輸出 Throwing MyException from f() MyException at ... at ... Throwing MyException from g() MyException: Originated in g() // 此即建立異常類型時傳入的String參數 at ... at ...
try { ... } catch( Exception e ) { // 填寫異常的基類,該catch子句通常置於末尾 ... }
Exception類型所持有的方法:函數
注意:從下往上每一個方法都比前一個提供了更多的異常信息!學習
printStackTrace()方法所提供的棧軌跡信息能夠經過getStackTrace()方法來Get,舉例:code
try { throw new Exception(); } catch( Exception e ) { for( StackTraceElement ste : e.getStackTrace() ) System.out.println( ste.getMethodName() ); }
這裏使用getMethodName()方法來給出異常棧軌跡所通過的方法名!orm
try { ... } catch( Exception e ) { throw e; // 從新拋出一個異常! }
若只是簡單地將異常從新拋出,則然後用printStackTrace()顯示的將是原異常拋出點的調用棧信息,而非從新拋出點的信息,欲更正該信息,可使用fillInStackTrace()方法:對象
try { ... } catch( Exception e ) { throw (Exception)e.fillInStackTrace(); // 該行就成了異常的新發生地! }
異常鏈:在捕獲一個異常後拋出另外一個異常,並但願將原始的異常信息保存下來!
解決辦法:
注意:Throwable子類中,僅三種基本的異常類提供了待cause參數的ctor(Error、Exception、RuntimeException),其他狀況只能靠initCause()方法,舉例:
class DynamicFieldsException extends Exception { } public Object setField( String id, Object value ) throws DynamicFieldsException { if( value == null ) { DynamicFieldsException dfe = new DynamicFieldsException(); dfe.initCause( new NullPointerException() ); throw dfe; } Object result = null; try { result = getField(id); } catch( NoSuchFieldException e ) { throw new RuntimeException( e ); } }
try { ... } catch(...) { ... } finally { // finally子句老是會被執行!!! ... }
使用時機:
try { ... } catch(...) { ... } finally { // finally子句老是會被執行!!! sw.off(); // 最後老是須要關掉某個開關! }
public static void func( int i ) { try { if( i==1 ) return; if( i==2 ) return; } finally { print( "Performing cleanup!" ); // 即便上面有不少return,但該句確定被執行 } }
finally存在的缺憾:兩種狀況下的finally使用會致使異常丟失!
// 異常類 class VeryImportantException extends Exception { poublic String toString() { return "A verfy important exception!"; } } class HoHumException extends Exception { public String toString() { return "A trivial exception!"; } } ------------------------------------------------------------------ // 使用異常的客戶端 public class LostMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main( String[] args ) { try { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); // 最後只會該異常生效,lm.f()拋出的異常丟了! } } catch( Exception e ) { System.out.println(e); } } } ----------------------------------------------------------------- // 輸出 A trivial exception!
public static void main( String[] args ) { try { throw new RuntimeException(); } finally { return; // 這將會掩蓋全部的異常拋出 } }
// 異常類 class A extends Exception { } class A1 extends A { } class A2 extends A { } class A1_1 extends A1 { } class B extends Exception { } class B1 extends B { } ------------------------------------------------- // 用了異常類的基類 abstract class Base { public Base() throws A { } public void event() throws A { } // (1) public abstract void atBat throws A1, A2; public void walk() { } } ------------------------------------------------- // 用了異常類的接口 interface Interf { public void event() throws B1; public void rainHard() throws B1; } ------------------------------------------------- // 繼承基類並實現接口的客戶端類 public class Ext extends Base implements Interf { public Ext() throws B1, A { } // (2) public Ext( String s ) throws A1, A {} // (2) public void walk() throws A1_1 { } // (3) 編譯錯誤! public void rainHard() throws B1 {} // (4) public void event() { } // (5) public void atBat() throws A1_1 { } // (6) public static void main( String[] args ) { try { Ext ext = new Ext(); ext.atBat(); } catch( A1_1 e ) { ... } catch( B1 e ) { ... } catch( A e ) { ... } try { Base base = new Ext(); ext.atBat(); } catch( A2 e ) { // 這裏的catch必須按照Base中函數的異常拋出來寫 ... } catch( A1 e ) { ... } catch( B1 e ) { ... } catch( A ) { ... } } }
上面的例子能夠總結以下:【注意對應數字標號】
對於在構造階段可能會拋出異常並要求清理的類,安全的方式是使用嵌套的try子句:即在建立須要清理的對象以後,當即進入一個try-finally塊,舉例:
特別須要注意的是下面的例子裏在ctor中對文件句柄的close應放置的合理位置!
// 須要清理的對象類 class InputFile { private BufferedReader in; InputFile( String fname ) throws Exception { // 構造函數! try { in = new BufferedReader( new FileReader(fname) ); // 這裏放置可能拋出異常的其餘代碼 } catch( FileNotFoundException e ) { // 若上面的FileReader異常,將會拋FileNotFoundException,走到這裏,該分支無需in.close()的 System.out.println( "Could not open " + fname ); throw e; } catch( Exception e ) { // 走到這裏其實說明in對象已經構建成功,這裏是必須in.close()的 try { in.close(); // 注意此處關閉動做單獨用try進行保障 } catch( IOException e2 ) { System.out.println("in.close() unsuccessful"); } throw e; } finally { // 注意in.close() 不要在此處關閉,由於try中假如BufferedReader構造失敗,此時in對象未生成成功,是無需close()一說的! } } String getLine() { String s; try { s = in.readLine(); } catch( IOException e ) { System.out.println( "readLine() unsuccessful!" ); s = "failed"; } return s; } void cleanup() { // 提供手動的關閉文件句柄的操做函數 try { in.close(); } catch( IOException e ) { System.out.println( "in.close() failed !" ); } } } ---------------------------------------------------- // 客戶端代碼 public class Cleanup { public static void main( String[] args ) { try { InputFile in = new InputFile( "Cleanup.java" ); try { // 上面InputFile構造完成之後當即進入該try-finally子句! String s = ""; int i = 1; while( (s = in.getLine()) != null ) System.out.println(""+ i++ + ": " + s); } catch( Exception e ) { e.printStackTrace( System.out ); } finally { // 該finally必定確保in能正常cleanup()! in.cleanup(); } } catch( Exception e ) { System.out.println( "InputFile ctor failed!" ); } } // end main() }