JAVA【異常二】異常處理機製
Java中異常提供了一種識別及響應錯誤情況的一致性機製,有效地異常處理能使程序更加健壯、易於調試。異常之所以是一種強大的調試手段,在於其回答了以下三個問題:
- 什麼出了錯?
- 在哪出的錯?
- 為什麼出錯?
在有效使用異常的情況下,異常類型回答了“什麼”被拋出,異常堆棧跟蹤回答了“在哪“拋出,異常信息回答了“為什麼“會拋出。
在Java 應用程序中,異常處理機製為:拋出異常,捕捉異常。
拋出異常:當一個方法出現錯誤引發異常時,方法創建異常對象並交付運行時係統,異常對象中包含了異常類型和異常出現時的程序狀態等異常信息。運行時係統負責尋找處置異常的代碼並執行。
捕獲異常:在方法拋出異常之後,運行時係統將轉為尋找合適的異常處理器(exception handler)。潛在的異常處理器是異常發生時依次存留在調用棧中的方法的集合。當異常處理器所能處理的異常類型與方法拋出的異常類型相符時,即為合適的異常處理器。運行時係統從發生異常的方法開始,依次回查調用棧中的方法,直至找到含有合適異常處理器的方法並執行。當運行時係統遍曆調用棧而未找到合適 的異常處理器,則運行時係統終止。同時,意味著Java程序的終止。
對於運行時異常、錯誤或可查異常,Java技術所要求的異常處理方式有所不同。
由於運行時異常的不可查性,為了更合理、更容易地實現應用程序,Java規定,運行時異常將由Java運行時係統自動拋出,允許應用程序忽略運行時異常。
對於方法運行中可能出現的Error,當運行方法不欲捕捉時,Java允許該方法不做任何拋出聲明。因為,大多數Error異常屬於永遠不能被允許發生的狀況,也屬於合理的應用程序不該捕捉的異常。
對於所有的可查異常,Java規定:一個方法必須捕捉,或者聲明拋出方法之外。也就是說,當一個方法選擇不捕捉可查異常時,它必須聲明將拋出異常。
能夠捕捉異常的方法,需要提供相符類型的異常處理器。所捕捉的異常,可能是由於自身語句所引發並拋出的異常,也可能是由某個調用的方法或者Java運行時係統拋出的異常。也就是說,一個方法所能捕捉的異常,一定是Java代碼在某處所拋出的異常。簡單地說,異常總是先被拋出,後被捕捉的。
Java異常的捕獲和處理是一個不容易把握的事情,如果處理不當,不但會讓程序代碼的可讀性大大降低, 而且導致係統性能低下,甚至引發一些難以發現的錯誤。
Java異常處理涉及到五個關鍵字,分別是:try、catch、finally、throw、throws。下麵將驟一介紹, 通過認識這五個關鍵字,掌握基本異常處理知識。
1、捕獲異常:try、catch和 finally
在Java中,異常通過try-catch語句捕獲。其一般語法形式為:
- 1. try {
- 2. // 被檢測的代碼
- 3. //可能出現的代碼
- 4. } catch (異常類名 變量){
- 5. // 異常的 處理方式
- 6. }
關鍵詞try後的一對大括號將一塊可能發生異常的代碼包起來,稱為監控區域。Java方法在運行過程中出現異常,則創建異常對象。將異常拋出監控區域之外,由Java運行時係統試圖尋找匹配的catch子句以捕獲異常。若有匹配的catch子句,則運行其異常處理代碼,try-catch語句結束。
匹配的原則是:如果拋出的異常對象屬於catch子句的異常類,或者屬於該異常類的子類,則認為生成的異常對象與catch塊捕獲的異常類型相匹配。
捕捉運行時係統自動拋出“除數為0”引發的ArithmeticException異常:
- public class ExceptionDemo1 {
- public static void main(String[] args) {
- int a = 10;
- int b = 0;
- try {
- int c=a/b;
- }
- catch (ArithmeticException e) {
- System.out.println("算術異常,除數為0");
- }
- }
- }
運行結果:算術異常,除數為0
2、 try-catch-finally
try-catch語句還可以包括第三部分,就是finally子句。它表示無論是否出現異常,都應當執行的內容。try-catch-finally語句的一般語法形式為:
- 1. try {
- 2. // 被檢測的代碼
- 3. //可能出現的代碼
- 4. } catch (異常類名 變量){
- 5. // 異常的 處理方式
- 6. } finally{
- 7. 必須要執行的代碼
- 8. }
try 塊:用於捕獲異常。其後可接零個或多個catch塊,如果沒有catch塊,則必須跟一個finally塊。
catch 塊:用於處理try捕獲到的異常。
finally 塊:無論是否捕獲或處理異常,finally塊裏的語句都會被執行。當在try塊或catch塊中遇到return語句時,finally語句塊將在方法返回之前被執行。在以下4種特殊情況下,finally塊不會被執行:
1)在finally語句塊中發生了異常。
2)在前麵的代碼中用了System.exit()退出程序。
3)程序所在的線程死亡。
4)關閉CPU。
捕捉數組下標越界異常:
- 9. public class ExceptionDemo2 {
- 10.
- 11. public static void main(String args[]) {
- 12.
- 13. int[] a = new int[3];
- 14.
- 15. try {
- 16. a[3] = 10;
- 17. } catch (ArrayIndexOutOfBoundsException e) {
- 18. System.out.println("數組下標越界異常");
- 19. } finally {
- 20. System.out.println("最終始終要執行");
- 21. }
- 22. }
- 23. }
運行結果:數組下標越界異常
最終始終要執行
3. try-catch-finally 規則(異常處理語句的語法規則):
1) 必須在try之後添加 catch 或 finally 塊。try 塊後可同時接 catch 和finally 塊,但至少有一個塊。
2) 必須遵循塊順序:若代碼同時使用 catch 和finally 塊,則必須將 catch 塊放在 try 塊之後。
3) catch 塊與相應的異常類的類型相關。
4) 一個 try 塊可能有多個 catch 塊。若如此,則執行第一個匹配塊。即Java虛擬機會把實際拋出的異常對象依次和各個catch代碼塊聲明的異常類型匹配,如果異常對象為某個異常類型或其子類的實例,就執行這個catch代碼塊,不會再執行其他的 catch代碼塊
5) 可嵌套 try-catch-finally 結構。
6) 在 try-catch-finally 結構中,可重新拋出異常。
7) 除了下列情況,總將執行 finally 做為結束:JVM 過早終止(調用 System.exit(int));在 finally 塊中拋出一個未處理的異常;計算機斷電、失火、或遭遇病毒攻擊。
4. try、catch、finally語句塊的執行順序:
1)當try沒有捕獲到異常時:try語句塊中的語句逐一被執行,程序將跳過catch語句塊,執行finally語句塊和其後的語句;
2)當try捕獲到異常,catch語句塊裏沒有處理此異常的情況:當try語句塊裏的某條語句出現異常時,而沒有處理此異常的catch語句塊時,此異常將會拋給JVM處理,finally語句塊裏的語句還是會被執行,但finally語句塊後的語句不會被執行;
3)當try捕獲到異常,catch語句塊裏有處理此異常的情況:在try語句塊中是按照順序來執行的,當執行到某一條語句出現異常時,程序將跳到catch語句塊,並與catch語句塊逐一匹配,找到與之對應的處理程序,其他的catch語句塊將不會被執行,而try語句塊中,出現異常之後的語句也不會被執行,catch語句塊執行完後,執行finally語句塊裏的語句,最後執行finally語句塊後的語句;
圖示try、catch、finally語句塊的執行順序:

二、 拋出異常
任何Java代碼都可以拋出異常,如:自己編寫的代碼、來自Java開發環境包中代碼,或者Java運行時係統。無論是誰,都可以通過Java的throw語句拋出異常。從方法中拋出的任何異常都必須使用throws子句。
1、拋出異常throw
Throw在方法內部,用來拋出一個Throwable類型的異常。程序會在throw語句後立即終止,它後麵的語句執行不到,然後在包含它的所有try塊中(可能在上層調用函數中)從裏向外尋找含有與其匹配的catch子句的try塊。
如果拋出了檢查異常,則還應該在方法頭部聲明方法可能拋出的異常類型。該方法的調用者也必須檢查處理拋出的異常。
如果所有方法都層層上拋獲取的異常,最終JVM會進行處理,處理也很簡單,就是打印異常消息和堆棧信息。如果拋出的是Error或RuntimeException,則該方法的調用者可選擇處理該異常。
拋出數組下標越界異常:
- public class ExceptionDemo3 {
- public static void main(String args[]) {
- int[] a = new int[3];
- try {
- a[3] = 10;
- } catch (ArrayIndexOutOfBoundsException e) {
- throw new ArrayIndexOutOfBoundsException("數組 下標越界異常了");
- }
- }
- }
運行結果:Exception in thread "main"java.lang.ArrayIndexOutOfBoundsException:數組下標越界異常了
atExceptionDemo3.main(ExceptionDemo3.java:11)
throw隻是拋出異常暫時不做處理,但是總有一個地方要處理異常。
2、throws 對異常的說明
如果一個方法可能會出現異常,但沒有能力處理這種異常,可以在方法聲明處用throws子句來聲明拋出異常。throws語句用在方法定義時聲明該方法要拋出的異常類型,如果拋出的是Exception異常類型,則該方法被聲明為拋出所有的異常,但不推薦直接使用Exception
當方法拋出異常列表的異常時,方法將不對這些類型及其子類類型的異常作處理,而拋向調用該方法的方法,由他去處理。如果調用者不想處理該異常,可以繼續向上拋出,但最終要有能夠處理該異常的調用者。
Throws拋出異常的規則:
1) 如果是不可查異常(unchecked exception),即Error、RuntimeException或它們的子類,那麼可以不使用throws關鍵字來聲明要拋出的異常,編譯仍能順利通過,但在運行時會被係統拋出。
2)必須聲明方法可拋出的任何可查異常(checked exception)。即如果一個方法可能出現受可查異常,要麼用try-catch語句捕獲,要麼用throws子句聲明將它拋出,否則會導致編譯錯誤
3)僅當拋出了異常,該方法的調用者才必須處理或者重新拋出該異常。當方法的調用者無力處理該異常的時候,應該繼續拋出,而不是囫圇吞棗。
4)調用方法必須遵循任何可查異常的處理和聲明規則。若覆蓋一個方法,則不能聲明與覆蓋方法不同的異常。聲明的任何異常必須是被覆蓋方法所聲明異常的同類或子類。
三、自定義異常
使用Java內置的異常類可以描述在編程時出現的大部分異常情況。除此之外,用戶還可以自定義異常。用戶自定義異常類,隻需繼承Exception類即可。
在程序中使用自定義異常類,大體可分為以下幾個步驟。
(1)創建自定義異常類。
(2)在方法中通過throw關鍵字拋出異常對象。
(3)如果在當前拋出異常的方法中處理異常,可以使用try-catch語句捕獲並處理;否則在方法的聲明處通過throws關鍵字指明要拋出給方法調用者的異常,繼續進行下一步操作。
(4)在出現異常方法的調用者中捕獲並處理異常。
- public class FuShuException extends Exception {
- public FuShuException() {
- super();
- }
- public FuShuException(String message) {
- super(message);
- }
- }
- public class ExceptionDemo {
- public static void main(String[] args) {
- int avg;
- try {
- avg = getAvg(50,60,-70,80);
- } catch (FuShuException e) {
- e.printStackTrace();
- }
- }
- //計算成績平均數
- private static int getAvg(int...source)throws FuShuException {
- int sum=0;
- for (int i : source) {
- if (i<0) {
- throw new FuShuException("成績錯誤:"+i);
- }
- sum=sum+i;
- }
- return sum/source.length;
- }
- }
運行結果:
FuShuException:成績錯誤:-70
atExceptionDemo.getAvg(ExceptionDemo.java:19)
atExceptionDemo.main(ExceptionDemo.java:8)
最後更新:2017-11-18 09:33:46