1. 程式人生 > >Java 異常的捕獲與處理詳解(一)

Java 異常的捕獲與處理詳解(一)

一、異常的產生

異常是程式之中導致程式中斷的一種指令流,異常一旦出現並且沒有進行合理處理的話,那麼程式就將中斷執行。
下面,通過兩個程式來進行異常產生問題的對比。
(1)不產生異常的程式:

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        int result = 10 / 2;
        System.out.println("2、除法計算結果:" + result);
        System.out
.println("3、除法計算結束。"); } }

執行結果:

1、除法計算開始。
2、除法計算結果:5
3、除法計算結束。

(2)產生異常的程式

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        int result = 10 / 0; // 會出現錯誤
        System.out.println("2、除法計算結果:" + result);
        System.out.println("3、除法計算結束。"
); } }

執行結果:

1、除法計算開始。Exception in thread "main" 
java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:4)

一旦產生異常,我們發現產生異常的語句以及之後的語句將不再執行,預設情況下是進行異常資訊的輸出,而後自動結束程式的執行。

現在,我們要做的是:即使程式出現了異常,也要讓程式正確的執行完畢。

二、異常的處理

如果希望程式出現異常之後程式依然可以正常的完成的話,那麼就可以使用如下的格式進行異常的處理:

try {
         可能出現異常的語句 ;
} [ catch (異常型別 異常物件) {
         處理異常 ;
} catch (異常型別 異常物件) {
         處理異常 ;
} ...
] [finally { 不管是否出現異常,都執行此程式碼 ; }]

現在,使用以上的操作處理異常處理前面除法於是出現的異常:

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        try {
            int result = 10 / 0; // 異常
            System.out.println("2、除法計算結果:" + result); // 之前語句有異常,此語句不再執行
        } catch (ArithmeticException e) {
            System.out.println(e); // 異常處理:輸出錯誤資訊,java.lang.ArithmeticException:/ by zero
        }
        System.out.println("3、除法計算結束。");
    }
}

執行結果:

1、除法計算開始。
java.lang.ArithmeticException: / by zero
3、除法計算結束。

可以發現,加入了異常處理之後,程式中即使有了異常,程式也可以正常的執行完畢,但是異常處理時的錯誤輸出資訊和之前相比,出錯的資訊不明確了,那麼為了讓錯誤的資訊更加的完整,一般都會呼叫printStackTrace()方法進行異常資訊的列印,這個方法列印的異常資訊是最完整的:

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        try {
            int result = 10 / 0; // 異常
            System.out.println("2、除法計算結果:" + result); // 之前語句有異常,此語句不再執行
        } catch (ArithmeticException e) {
            e.printStackTrace(); // 異常處理:輸出錯誤資訊
        }
        System.out.println("3、除法計算結束。");
    }
}

執行結果:

1、除法計算開始。
java.lang.ArithmeticException: / by zero
    at Test.main(Test.java:5)
3、除法計算結束。

此時發現,列印的資訊是很完整的。

除了try…catch格式處理異常外,還可以使用try…catch..finally:

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        try {
            int result = 10 / 1;
            System.out.println("2、除法計算結果:" + result);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("不管是否出現異常都執行");
        }
        System.out.println("3、除法計算結束。");
    }
}

執行結果:

1、除法計算開始。
2、除法計算結果:10
不管是否出現異常都執行
3、除法計算結束。

但是,對於之前的程式又有了問題:現在執行數學計算的兩個引數,都是由程式預設提供,那麼如果說現在兩個計算的引數通過初始化引數傳遞呢?

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        try {
            int x = Integer.parseInt(args[0]); // 接收引數
            int y = Integer.parseInt(args[1]); // 接收引數
            int result = x / y;
            System.out.println("2、除法計算結果:" + result);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("不管是否出現異常都執行");
        }
        System.out.println("3、除法計算結束。");
    }
}

這個時候,資料由外部傳送,那麼就有可能出現以下幾類問題:
(1)執行時不輸入引數(java TestDemo),ArrayIndexOutOfBoundsException,未處理;
(2)輸入的引數不是數字(java TestDemo a b),NumberFormatException,未處理;
(3)被除數為0(java TestDemo 10 0),ArithmeticException,已處理。
可以發現,以上的程式實際上是存在三種異常,而程式之中只能夠處理一種,而對於不能處理的異常,發現程式依然會直接中斷執行。
加入多個catch:

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            int result = x / y;
            System.out.println("2、除法計算結果:" + result);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } catch (ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        } catch (NumberFormatException e) {
            e.printStackTrace();
        } finally {
            System.out.println("不管是否出現異常都執行");
        }
        System.out.println("3、除法計算結束。");
    }
}

現在,程式比之前更健壯了。

三、異常的處理流程

以上已經完成了異常的基本處理,但是所有的異常都像之前那樣一條條的判斷似乎是一件不可能完成的任務,因為日後肯定會接觸到一些不常見的異常資訊,那麼下面就必須首先研究異常的流程和結構。

先檢視兩個異常類的繼承結構:
(1)ArithmeticException:

java.lang.Object
    |- java.lang.Throwable
         |- java.lang.Exception
              |- java.lang.RuntimeException
                 |- java.lang.ArithmeticException

(2)ArrayIndexOutOfBoundsException:

java.lang.Object
   |- java.lang.Throwable
      |- java.lang.Exception
         |- java.lang.RuntimeException
             |- java.lang.IndexOutOfBoundsException
                |-java.lang.ArrayIndexOutOfBoundsException

可以發現,所有的異常型別最高的繼承類是Throwable,Throwable下有兩個子類:
(1)Error:指的是JVM錯誤,這個時候的程式並沒有執行,無法處理;
(2)Exception:指的是程式之中出現的錯誤資訊,可以進行異常處理。

通過繼承關係可以發現,在進行日後異常處理的時候是以Exception為主,並且可以形成以下的異常處理流程:

11

(1)如果程式中產生了異常,那麼JVM根據異常的型別,例項化一個指定異常類的物件;

(2)如果這時程式中沒有任何的異常處理操作,則這個異常類的例項化物件將交給JVM進行處理,而JVM的預設處理方式就是進行異常資訊的輸出,而後中斷程式執行;

(3)如果程式中存在了異常處理,則會由try語句捕獲產生的異常類物件;

(4)與try之後的每一個catch進行匹配,如果匹配成功,則使用指定的catch進行處理,如果沒有匹配成功,則向後面的catch繼續匹配,如果沒有任何的catch匹配成功,則這個時候將交給JVM執行預設處理;

(5)不管是否有異常都會執行finally程式,如果此時沒有異常,執行完finally,則會繼續執行程式之中的其他程式碼,如果此時有異常沒有能夠處理(沒有一個catch可以滿足),那麼也會執行finally,但是執行完finally之後,將預設交給JVM進行異常的資訊輸出,並且程式中斷。

通過以上的分析可以發現,實際上catch捕獲異常型別的操作,就和方法接收引數是一樣的,那麼按照之前所學習過的物件多型性來講,所有的異常類都是Exception的子類,那麼這個時候,實際上所有的異常都可以使用Exception進行接收:

public class Test {
    public static void main(String args[]) {
        System.out.println("1、除法計算開始。");
        try {
            int x = Integer.parseInt(args[0]);
            int y = Integer.parseInt(args[1]);
            int result = x / y;
            System.out.println("2、除法計算結果:" + result);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("不管是否出現異常都執行");
        }
        System.out.println("3、除法計算結束。");
    }
}

這時應該可以感受到異常處理所帶來的好處了。但是這種操作也存在一種問題:如果在一些異常處理要求嚴格的專案之中,異常必須分別處理,如果現在異常的處理要求不是很嚴格,直接編寫Exception就足夠了。

未完待續。。。