異常概述java
異常也稱爲例外,是在程序運行過程當中發生的而且會打斷程序正常執行的事件,好比算術異常、空指針異常、文件找不到異常。因此在程序設計時,必須考慮到可能發生的異常事件,並作出相應的處理。這樣才能保證程序能夠正常運行。數組
Java的異常處理機制也秉承着面向對象的基本思想,在Java中,全部的異常都是以類的形式存在,除了內置的異常類以外,Java也能夠自定義異常類。此外,Java的異常處理機制也容許自定義拋出異常。網絡
爲何要使用異常架構
在沒有異常處理的語言中,就必須使用if或switch等語句,配合所想獲得的錯誤情況來捕捉程序裏全部可能發生的錯誤。但爲了捕捉這些錯誤,編寫出來的代碼常常有不少的if語句,有時候這樣也未必能捕捉到全部的錯誤,並且這樣作勢必致使程序運行效率的下降。Java的異常處理機制剛好改進了這一點,它易於使用而且可自行定義異常類,可是不得不說,異常也是一個較爲耗費性能的操做,所以咱們須要合理地利用Java的異常處理機制,以增進程序的穩定性和效率。性能
異常的繼承架構spa
異常可分爲兩大類:java.lang.Exception與java.lang.Error,這兩個類均繼承自java.lang.Throwable,上圖爲Throwable類的繼承關係圖。設計
習慣上將Error與Exception統稱爲異常類,但這二者本質上仍是有不一樣的:指針
一、Error專門用來處理嚴重影響程序運行的錯誤,但是一般程序設計者不會涉及程序代碼去捕捉這種錯誤,其緣由在於即便捕捉到它,也沒法給予適當的處理,好比Java虛擬機出錯就屬於一種Errorcode
二、Exception包含了通常性的異常,這些異常一般在捕捉到以後即可以作妥善的處理,以確保程序繼續運行對象
從繼承架構圖中能夠看出,Exception擴展出數個子類,其中IOException、RuntimeException是較經常使用的兩種。RuntimeException即便不編寫異常處理的程序代碼,依然能夠編譯成功,而這種異常必須是在程序運行時纔有可能發生,好比數組索引值超出了範圍。與RuntimeException不一樣的是,IOException必定要編寫異常處理的程序代碼才行,它一般用來處理輸入/輸出相關的操做,如對文件的訪問、網絡的鏈接等。
當異常發生時,發生異常的語句代碼會拋出一個異常類的實例化對象,以後此對象與catch語句中的類的類型進行匹配,而後在相應的catch中進行處理。
try、catch、finally、throw、throws
當異常發生時,一般能夠用兩種方法來處理:
一、交由Java默認的異常處理機制作處理,但這種處理方式,Java一般只能輸出異常信息,接着便終止程序的運行,好比:
1 public static void main(String[] args) throws InterruptedException 2 { 3 operateNull(null); 4 System.out.println("After operateNull"); 5 } 6 7 public static char operateNull(String str) 8 { 9 return str.charAt(0); 10 }
返回結果爲:
Exception in thread "main" java.lang.NullPointerException at com.xrq.test33.TestMain.operateNull(TestMain.java:13) at com.xrq.test33.TestMain.main(TestMain.java:7)
和"接着便終止程序的運行"一致,由於第4行的語句並無打印。
二、自行編寫try-catch-finally塊來捕捉異常,這種寫法的最大好處是,能夠靈活操控程序的流程且可作出最適當的處理,好比:
1 public static void main(String[] args) throws InterruptedException 2 { 3 try 4 { 5 Thread.sleep(1000); 6 operateNull(null); 7 } 8 catch (NullPointerException e) 9 { 10 System.out.println("NullPointerException's catch block!"); 11 throw e; 12 } 13 finally 14 { 15 System.out.println("NullPointerException's finally block!"); 16 } 17 } 18 19 public static char operateNull(String str) 20 { 21 return str.charAt(0); 22 }
返回結果爲:
1 NullPointerException's catch block! 2 NullPointerException's finally block! 3 Exception in thread "main" java.lang.NullPointerException 4 at com.xrq.test33.TestMain.operateNull(TestMain.java:25) 5 at com.xrq.test33.TestMain.main(TestMain.java:10)
關於這段代碼解釋每一個關鍵字:
(1)try表示要檢查的程序語句
(2)catch表示異常發生時的處理語句,可省略
(3)finally表示不管catch內部寫了什麼,即便是return,也都會運行到的語句,可省略。finally經常使用於對某段待檢查的代碼作掃尾工做,好比ReentrantLock的unlock()、IO的close()
(4)throw表示拋出一個類的實例,注意實例二字,實例意味着throw出去的是一個實例化的異常對象。因此代碼的11行也能夠寫爲"throw new NullPointerException()",至於爲何不這麼寫,由於寫"throw new NullPointerException()"並無返回結果中第4行、第5行中的異常堆棧。注意,throw關鍵字能夠寫在任何地方,並不強制必須寫在catch塊中,運行到throw所在的行,打印異常並當即退出當前方法
(5)throws用於方法聲明,表示若是方法內的程序代碼可能會發生異常,且方法內又沒有使用代碼的代碼塊來捕捉這些異常時,則必須在聲明方法時一併指明全部可能發生的異常,以便讓調用此方法的程序得以作好準備來捕捉異常。好比"方法名稱(參數...) throws 異常類1, 異常類2, 異常類3...",也能夠不這麼麻煩,Exception是全部異常的父類,所以也能夠直接"方法名稱(參數...) throws Exception"
異常場景彙總
異常細節諸多,以前我也一直有搞不清楚的地方,所以這裏對多種場景作一個總結、彙總:
一、接口方法能夠throws異常,但必須throws一個具體的異常,不能直接throws出去Exception
public interface InterfaceException { void ExceptionMethod() throws NullPointerException; }
二、接口方法throws異常,其實現類實現該方法的時候不強制必須拋出該異常,也能夠任意拋出異常
public class InterfaceExceptionImpl implements InterfaceException { public void ExceptionMethod() { } }
public class InterfaceExceptionImpl implements InterfaceException { public void ExceptionMethod() throws ArrayIndexOutOfBoundsException { } }
三、catch塊內若是捕獲到了異常而且throw出去了e,那麼方法以後的代碼都不會再運行了;catch塊內若是捕獲到了異常,可是沒有throw出去e,也沒有任何致使程序終止的語句,那麼try...catch...finally以後的語句仍然能夠繼續運行
四、捕獲異常不能夠先catch (Exception e){...}再catch (NullPointerException e)
緣由是"Unreachable catch block for NullPointerException. It is already handled by the catch block for Exception",即Java檢測到NullPointer這個異常是不可達的
五、方法A聲明throws異常,則調用方法A的代碼必須try...catch...該異常
六、方法A聲明throws出Exception,調用方法A的代碼必須try...catch...該異常,若是:
(1)有匹配異常的catch塊,則優先走匹配異常的catch塊
(2)若是沒有匹配異常的catch塊,可是有catch (Exception e){...},則走catch (Exception e){...}
(3)若是沒有沒有匹配的catch塊,則調用方法A的地方throw異常,方法終止
七、若是方法中沒有try...catch異常而該方法發生了異常,且方法聲明中有throws,那麼發生的該異常能夠被拋到調用方法的代碼中
八、若是方法中對某個代碼塊作了try...catch而且想把catch到的異常拋給調用方法的地方,那麼:
(1)不能夠只有throw沒有throws,這將會致使捕獲到的異常沒法被拋給調用方法的地方
(2)不能夠只有throws沒有throw,這將會致使編譯出錯
只有在catch塊中throw捕獲到的異常而且在方法聲明的地方throws異常,才能夠將一個異常正確地拋給調用方法的地方