1. 程式人生 > >異常的捕獲與處理

異常的捕獲與處理

一、異常是什麼?
異常的定義:異常是導致一個程式中斷的指令流,一旦出現之後程式就立即退出。
例如:除數為0

        int a = 10;
        int b = 0;
        System.out.println(a + "/" + b + "=" + a/b);
        System.out.println("運算結束");

程式執行結果:

Exception in thread "main" java.lang.ArithmeticException: / by zero
    at ch1.Test1.main(Test1.java:8)

因此,一旦發生了異常後,程式將不再向下執行,直接退出程式.並輸出異常資訊。
為了保證程式中即使出現異常了可以處理異常並繼續執行,那麼就需要使用異常處理語句了。

二、怎麼處理異常?
1、在Java中處理異常的語句:

        try {
            System.out.println(a + "/" + b + "=" + a/b);
            //可能出現異常的語句
        }catch(異常型別 異常物件){
            //在這裡處理異常1
        }catch(異常型別 異常物件){
            //在這裡處理異常2
            //......可以捕獲多個異常物件
        } finally {
            //異常處理的統一出口,不管是否有異常、是否處理了異常都會執行
}

2、因此可以使用try…catch…finally 語句處理剛剛出現的異常:

        int a = 10;
        int b = 0;
        try {
            //可能出現異常的語句
            System.out.println(a + "/" + b + "=" + a/b);
        }catch(ArithmeticException e){
            //在這裡處理異常
            System.err.println("資料有誤");
        } finally {
            System.out.println("執行了finally塊"
); } System.out.println("運算結束");

輸出結果為:

資料有誤
執行了finally塊
運算結束

由於程式中添加了異常處理機制,所以此時的程式可以正常執行完畢,其中可以加入finally語句塊作為統一的出口操作(不管是否有異常、是否處理了異常都會執行)。
3、finally用來做什麼(作用):
對於沒有垃圾回收機制和結構函式自動呼叫機制(解構函式:當物件不再被使用時會被呼叫的函式)的語言來說,finally語句非常重要,它能使程式設計師保證:無論try塊裡發生了什麼,記憶體總能得到釋放。但是Java有垃圾回收機制,所以記憶體釋放不再是問題,另外,java也沒有解構函式可供呼叫。那麼在什麼情況下才能用到finally呢?
當然是要把除記憶體之外的資源恢復到出事狀態時。這種需要清理的資源包括:已開啟的檔案或者網路連線,在螢幕上畫的圖形,甚至可以是外部的某個開關。

三、異常的處理流程
異常的處理流程
1. 當異常產生後,JVM自動生成一個異常類的例項化物件,如果此時編寫有了異常處理語句,則進行異常處理,否則交由JVM進行處理(即輸出異常資訊並結束程式的執行)。
2. 使用try語句捕獲異常物件後,自動與catch中的異常型別相匹配,如果匹配成功,則表示可以使用catch塊中的語句處理異常,如果都沒有匹配成功,則交由JVM進行處理。
3. 如果存在finally語句塊,程式中不管是否出現了異常,都會執行該語句塊中的程式碼。

四、異常的匹配
丟擲異常物件後,異常處理機制會按照catch的順序匹配出“最近”的異常物件。找到匹配的處理程式之後,異常處理機制就認為異常得到了處理,就不會繼續查詢(區別於switch,switch需要break進行跳出匹配)。
值得注意的是:
1、查詢的時候並不要求丟擲的異常物件與處理程式所宣告的異常物件完全匹配,派生類的物件也可以匹配其基類(父類)的處理程式。就如:

        try {
            throw new NumberFormatException();//在此丟擲一個異常物件
        } catch (Exception e) {
            e.printStackTrace();
        }

2、編譯器不允許把範圍大的異常物件放在前面,假如捕獲到了Exception異常物件,那麼就停止捕獲,而作為其子類的處理程式永遠得不到執行。就如:

try {
            throw new NumberFormatException();
        } catch (Exception e) { //這裡編譯器報錯,從精確的異常匹配更大的異常。
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

五、throws 與 throw關鍵字

1、throws關鍵字作用於方法的宣告上,表示一個方法不處理異常,而交由呼叫處進行處理。

class MyMath {
    public int division(int num1, int num2) throws Exception {
        return num1/num2;
    }
}
public class MyMathTest {
    public static void main(String args[]) {
        //呼叫division(),如果不進行異常處理,則編譯不能通過(但是有特例)
        try {
            System.out.println(new MyMath().division(10,0));
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

throws關鍵字不僅可以在普通方法上使用,mian方法上也可以使用。即mian方法不對異常進行處理,而向它的上級丟擲,即由JVM進行處理。
實際上預設情況下所有異常都是交由JVM進行處理的,比如(一)中。

2、throw關鍵字:在程式中人為丟擲一個異常物件。

        try {
            throw new Exception("我自己拋的異常");
        } catch (Exception e) {
            e.printStackTrace();
        }
        /*
         OutPut:
         java.lang.Exception: 我自己拋的異常
            at ch1.Test1.main(Test1.java:22)
         */

實際使用中較少。

3、throws 與 throw的區別:

  • throws:在方法的宣告上使用,表示此方法在呼叫時必須進行異常處理
  • throw:指的是在方法中人為丟擲一個異常類的例項化物件(這個物件可以是自定義的或者已經存在的)

六、RuntimeException——“不受檢查異常”
在(五、1)的程式碼中就有註釋:如果不進行異常處理,則編譯不能通過(但是有特例),現在就來講這個特例。
引入:如果將一根字串變為整數型別,我們可以使用Integer類中的parseInt()方法實現。
檢視Java API可知parseInt()方法:

public static int parseInt(String s, int radix) throws NumberFormatException

本方法存在了throws關鍵字的宣告,理論上講,在呼叫時必須進行異常處理,可實際的使用中可以不使用try…catch處理也可以編譯通過。

public class Test {
    public static void main(String[] args) {
        int a = Integer.parseInt("10");
    }
}

要想了解這個問題,就必須觀察NumberFormatException異常的繼承結構:
NumberFormatException異常的繼承結構
可見NumberFormatException是RuntimeException的子類,Java為了異常的處理方便,定義出了一個特殊的異常類——RuntimeException,一旦丟擲異常是此類或者此類的子類,那麼可以不用進行異常處理。如果不做任何處理,則一旦出現異常後將交由呼叫處理處進行處理。

RuntimException和Exception的區別:Exception必須處理,RuntimException可以不用處理。

七、異常處理模型
異常處理理論上有兩種基本模型:終止模型、恢復模型
1、終止模型:程式無法返回到異常發生的地方繼續執行,一旦異常被丟擲,就表明錯誤已經無法挽回,也不能回來繼續執行。
2、恢復模型:意思是在異常處理中修正錯誤,然後重新嘗試調用出問題的程式碼或者方法,並認為第二次能成功。對於恢復模型,通常系統異常被處理之後能繼續執行程式,如果要實現該模型,那麼在遇到錯誤時不能丟擲異常,而是呼叫方法來修正該錯誤。或者,把try塊放在while迴圈裡面,並在try塊後新增break退出語句(直到達到滿意的結果即退出)。

異常表面上看,恢復模型比終止模型好。但實際上程式設計師更喜歡使用“終止模型”的程式碼,而忽略恢復的行為。其中主要原因可能是它所導致的耦合:恢復性的處理程式需要了解異常丟擲的地點,這將依賴於丟擲位置的非通用性程式碼,這增加了程式碼編寫和維護的困難,更別說異常可能從許多地方丟擲的大型程式。

八、總結:
異常是Java程式設計補課分割的一部分,如果不瞭解如何使用它們,那你只能完成很有限的工作。我們不應把Java的異常處理機制當成是單一用途的工具。是的,他被設計出來處理一些煩人的執行時錯誤,這些錯誤往往是由程式碼控制能力之外的因素導致的。然而,它對於發現某些編譯器無法檢測到的錯誤,也是非常重要的。

注:個人知識有限,文章有錯誤或者不足之處歡迎指正。