25、異常捕獲與處理

合理使用異常處理,可以讓程序更加健壯。

異常的產生

異常是導致程序中斷執行的一種指令流。當異常出現時,如果沒有合理處理,程序就會中斷執行。
範例:不產生異常的代碼

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法計算開始");
        System.out.println("2.出發計算:10 / 2 = " + (10 / 2));
        System.out.println("3.除法計算結束");
    }
}

範例:產生異常

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法計算開始");
        // 2.中將出現異常
        System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
        System.out.println("3.除法計算結束");
    }
}

// 結果爲:
// 1.除法計算開始
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at com.java.util.Demo.main(Demo.java:7)

異常產生後,產生異常的語句以及之後的語句將不再執行。默認情況下系統會輸出異常信息,而後自動結束程序的執行。

異常的處理

1、Java中進行處理異常,使用trycatchfinally這三個關鍵字,語法如下:

try {
    // 可能出現異常的語句
} catch (異常類型 對象1) {
    // 異常處理
} catch (異常類型 對象2) {
    // 異常處理
} finally {
    // 不論是否出現異常都執行的語句
}

對於上述操作的組合有:try…catchtry…catch…finallytry…finally(這個不建議使用)。
範例:應用異常處理格式

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法計算開始");
        try {
            System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
        } catch (ArithmeticException e) {

        }
        System.out.println("3.除法計算結束");
    }
}

// 結果爲:
// 1.除法計算開始
// 3.除法計算結束

2、出現異常就要處理異常,爲了能進行異常處理,可以使用異常類中的printStackTrace()輸出完整的異常信息:

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法計算開始");
        try {
            System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
        } catch (ArithmeticException e) {
            e.printStackTrace();
        }
        System.out.println("3.除法計算結束");
    }
}

// 結果爲:
// 1.除法計算開始
// java.lang.ArithmeticException: / by zero
// at com.java.util.Demo.main(Demo.java:7)
// 3.除法計算結束

範例:使用try…catch…finally

public class Demo {
    public static void main(String[] args) {
        System.out.println("1.除法計算開始");
        try {
            System.out.println("2.出發計算:10 / 2 = " + (10 / 0));
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
            System.out.println("不論是否異常,都執行");
        }
        System.out.println("3.除法計算結束");
    }
}

3、異常捕獲時,一個try語句可以跟多個catch語句。

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

上述程序將由用戶輸入數據,可能存在以下異常:
· 用戶執行時不輸入參數(java Demo):java.lang.ArrayIndexOutOfBoundsException數組越界錯誤;
· 輸入的數據不是數字(java Demo a b):java.lang.NumberFormatException
· 被除數爲0(java Demo 10 0):java.lang.ArithmeticException
以上代碼只有一個catch,只能處理一個異常,其他異常依然會導致程序中斷
範例:增加多個catch

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

異常處理流程

1、觀察兩個異常類的繼承結構:

NumberFormatException:	
java.lang.Object
  |- java.lang.Throwable
      |- Exception
          |- RuntimeException
              |- IllegalArgumentException
                  |- NumberFormatException	
ArithmeticException:
java.lang.Object
  |-java.lang.Throwable
      |- Exception
          |- RuntimeException
              |- ArithmeticException

由上表可得,所有異常類都是Throwable的子類。Throwable下有兩個子類ErrorException

請解釋Error和Exception的區別:
· Error:指的是JVM錯誤,即:此時程序還沒有執行,用戶不能處理;
· Exception:指的是程序運行中產生的異常,用戶可以處理。

所謂的異常處理指的是Exception以及它的子類異常。
2、異常處理流程圖
在這裏插入圖片描述

流程描述:
1)當程序運行時出現異常,由JVM自動根據異常類型實例化一個與之類型匹配的異常類對象;
2)產生異常對象後,會判斷當前語句是否存在異常處理。如果沒有異常處理,就交給JVM進行默認的異常處理(輸出異常信息,結束程序調用);
3)如果有異常捕獲操作,會由try語句捕獲產生的異常類實例化對象,之後與catch語句進行比較,如果有符合的捕獲類型,則使用catch語句進行異常處理;如果不匹配,則繼續向下匹配其它catch語句;
4)不論異常處理是否能夠匹配,都要繼續執行,如果程序中存在finally語句,就先執行finally語句中的代碼,執行完畢後根據之前catch匹配結果來決定如何執行,如果之前成功捕獲異常,那就繼續執行finally之後的語句;如果沒有成功捕獲,就交給JVM進行默認處理。
整個過程和catch中的異常類型進行匹配,但是所有Java對象都可以自動向上轉型。即如果真的要匹配類型,簡單的做法就是匹配Exception。

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

上述將所有的異常都交由Exception類處理,因此程序無法知道具體產生的是什麼異常。

說明:
· 使用多個catch時,範圍大的異常一定要放在範圍小的異常後面,否則會出現語法錯誤。
· 直接捕獲Exception比較方便,但不合理,因爲所有異常都按照同種方式處理。項目中應根據具體異常類型處理。

throws關鍵字

1、throws關鍵字主要用於方法聲明,將異常交由被調用處(如main方法)處理。
範例:使用throws關鍵字

class MyMath {
    public static int div(int x, int y) throws Exception {
        // 使用了throws,所以該方法產生的異常交由調用處處理
        return x / y;
    }
}

範例:調用上述方法

public class Demo {
    public static void main(String[] args) {
        // 必須進行異常處理,否則代碼報錯
        try {
            System.out.println(MyMath.div(10, 2));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

調用了具有throws聲明的方法,不論操作是否異常,都需要使用try..catch進行異常處理。
2.在主方法使用throws關鍵字後,異常將交給JVM處理,即採用默認處理方式。由於開發的程序多數希望正常結束調用,因此主方法不應該使用throws關鍵字。

public class Demo {
    public static void main(String[] args) throws Exception {
        System.out.println(MyMath.div(10, 0));
    }
}

throw關鍵字

1、程序中可以使用throw手工拋出一個異常類的對象。

public class Demo {
    public static void main(String[] args) {
        try {
            throw new Exception("自定義異常");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

throws與throw的區別:
·throw:在方法中手工拋出一個異常類對象(該對象可以是自定義的,或者已經存在的);
·throws:用於方法聲明上,使得調用該方法時必須處理異常。

異常處理標準格式

要求:定義div(),在執行除法前打印提示信息,在計算結束後打印提示信息;如果計算中產生了異常,交給調用處處理。
範例:

class MyMath {
    public static void div(int x, int y) {
        System.out.println("==== 除法計算開始 ====");
        System.out.println(x / y);
        System.out.println("==== 除法計算結束 ====");
    }
}

public class Demo {
    public static void main(String[] args) {
        MyMath.div(10,2);
    }
}

上述代碼可能出現異常,因此要進行異常處理。根據要求,異常交由調用處處理,因此使用throws關鍵字。

class MyMath {
    // 如果div()出現異常,異常交給調用處處理
    public static void div(int x, int y) throws Exception {
        System.out.println("==== 除法計算開始 ====");
        System.out.println(x / y);
        System.out.println("==== 除法計算結束 ====");
    }
}

public class Demo {
    public static void main(String[] args) {
        try {
            MyMath.div(10, 2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代碼產生錯誤後,程序運行到System.out.println("==== 除法計算結束 ====");就不執行了。
範例:正確做法如下:

class MyMath {
    // 如果div()出現異常,異常交給調用處處理
    public static void div(int x, int y) throws Exception {
        System.out.println("==== 除法計算開始 ====");
        try {
            System.out.println(x / y);
        } catch (Exception e) {
            throw e; // 拋出異常
        } finally {
            System.out.println("==== 除法計算結束 ====");
        }
    }
}

public class Demo {
    public static void main(String[] args) {
        try {
            MyMath.div(10, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

RuntimeException類

範例:觀察下述程序

public class Demo {
    public static void main(String[] args) {
        int temp = Integer.parseInt("100");
    }
}

parseInt(): public static int parseInt(String s) throws NumberFormatException;
parseInt()拋出了NumberFormatException,按照之前知識點,此處應強制進行異常捕獲,但實際並沒有該要求:
觀察一下NumberFormatException的繼承結構:

java.lang.Object
  |- java.lang.Throwable
      |- java.lang.Exception
          |- java.lang.RuntimeException → 運行時異常
              |- java.lang.IllegalArgumentException
                  |- java.lang.NumberFormatException

Java爲方便代碼編寫,提供了RuntimeException類,該類的特徵是:程序在編譯時,不會強制性要求用戶處理異常,用戶可以根據自己的需求選擇性處理,但是沒有處理又發生異常,就會交給JVM默認處理。

請解釋Exception與RuntimeException的區別,請列舉常見的幾種RuntimeException
· Exception是RuntimeExceptio的父類;
· 使用Exception定義的異常必須要被處理,而RuntimeException的異常可以選擇性處理。
·常見的RuntimeException:ArithmeticException、NullPointerException、ClassCastException。

異常的捕獲及處理(斷言)

assert關鍵字在JDK1.4引入,其功能是進行斷言。

public class Demo {
    public static void main(String[] args) {
        int num = 10;
        assert num == 20 : "num 不等於20";
        System.out.println("num = " + num);
    }
}

默認情況下,斷言是不應該影響程序的運行,即Java在解釋程序時,斷言是默認不起作用的。
啓用斷言:java -ea Demo

Exception in thread "main" java.lang.AssertionError: num的內容不是20
        at Demo.main(Demo.java:6)

異常的捕獲及處理(自定義異常)

  1. Java自身提供了大量的異常類,但對於實際開發是不夠。例如:進行添加數據操作時,可能出現錯誤數據,錯誤數據出現就應該拋出異常,例如AddException,而該異常Java沒有,需要自己開發。
    如果要自己開發一個異常類可以選擇繼承Exception或RuntimeException。
    範例:定義AddException
class AddException extends Exception {
    public AddException(String msg) {
        super(msg);
    }
}

public class Demo {
    public static void main(String[] args) {
        int num = 11;
        try {
            if (num > 10) {
                throw new AddException("數值傳遞過大");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

上述代碼,只是介紹自定義異常的形式,不能說明自定義異常的作用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章