Java 異常處理

JiawenZhang97發表於2020-11-12
  • Java 的異常機智主要依賴於 try、catch、finally、throw、throws;

一、異常處理機制

1. 使用 try…catch 捕獲異常

try{
	//業務實現程式碼
}
catch (Exception e){
	alter 輸入不合法
	goto retry
}
  • 執行 try 塊裡的程式碼時出現異常,系統自動生成一個異常物件;該異常物件被提交給 Java 執行時環境,這個過程被稱為 丟擲(throw)異常
  • 當 Java 執行時環境收到異常物件時,會尋找能處理該異常物件的 catch 塊;
    -如果能找到合適的 catch 塊,就把該異常物件交給該 catch 塊處理,這個過程被稱為捕獲(catch)異常
    -如果找不到捕獲異常的 catch 塊,則執行時環境終止,Java 程式也將退出;

2. 異常類的繼承體系

  • try 塊後可以有多個 catch 塊;如果 try 塊被執行一次則try 塊後只有一個 catch 塊會被執行,不可能有多個 catch 塊被執行;
  • 常見的幾種異常:
IndexOutOfBoundException: 陣列越界異常
NumberFormatExpection: 數字格式異常
ArithmeticExpection:0 異常
  • 注意:所有父類異常的 catch 塊都應該排在子類異常 catch 塊的後面;

3. Java7 提供的多異常捕獲

  • 使用一個 catch 塊捕獲多種型別的異常需要注意:
    ①多種異常型別之間用 豎線(|)隔開;
    ②異常變數有隱式的 final 修飾,不能對異常變數重新賦值;

4. 訪問異常資訊

  • 在 catch 塊中訪問異常物件的相關資訊;

5. 使用 finally 回收資源

  • 在 try 塊裡開啟了一些物理資源,都必須顯式回收,所以異常處理機制提供了 finally 塊;
  • 不管 try 塊中的程式碼是否出現異常,也不管哪一個 catch 塊被執行,甚至在 try 塊或 catch 塊中執行了 return 語句,finally 塊總會被執行
  • 異常處理語法中,只有 try 塊是必須的,catch 和 finally 塊都是可選的,但兩者至少出現一個;其中 finally 塊必須位於所有的 catch 塊之後
  • 注意:如果 catch 塊中有 return 語句,但一定會先執行 finally 塊裡的程式碼;如果在 catch 塊中有 System.exit(1) 語句來退出虛擬機器,則 finally 塊將會失去執行的機會

6. 異常處理的巢狀

  • 完整的異常處理流程可以放在 try塊、catch 塊、finally 塊;

7. Java7 的自動關閉資源的 try 語句

  • 在 try 後緊跟一對圓括號,圓括號可以宣告、初始化一個或多個資源;(此處的資源指的是那些必須在程式結束時顯示關閉的資源),try 語句在該語句結束時自動關閉這些資源;
  • 自動關閉資源的 try 語句相當於包含了隱式的 finally 塊,因此這個 try 語句可以既沒有 catch 塊,也沒有 finally 塊;

二、Checked 異常和 Runtime 異常體系

  • Java 的異常被分為兩大類:Checked 異常和 Runtime 異常;
    所有 RuntimeException 類及其子類的例項被稱為 Runtime 異常;
    其餘的異常例項則被稱為 Checked 異常;
  • Checked 異常的處理方式
    ①當前方法明確知道如何處理該異常,程式用 try…catch 塊來捕獲該異常;
    ②當前方法不知道如何處理這種異常,應該在定義該方法時宣告丟擲該異常
  • Runtime 異常無需顯示宣告丟擲,如果程式需要捕獲 Runtime 異常,也可以使用 try…catch 塊來實現;

1. 使用 throws 宣告丟擲異常

  • 使用 throws 丟擲異常思路:當前方法不知道如何處理這種型別的異常,該異常應該由上一級呼叫者處理;
  • throws 宣告丟擲只能在方法簽名中使用,throws 可以宣告丟擲多個異常類,多個異常類之間以逗號隔開;
public class ThrowsTest {
    public static void main(String[] args) throws Exception{
        test();
    }
    public static void test() throws IOException{
        FileInputStream fis = new FileInputStream("D:\\a.txt");
//        System.out.println(fis.read());
    }
}
  • 程式碼分析
    因為 test() 方法宣告丟擲異常,所以呼叫該方法的程式碼要麼處於 try…catch 塊中,要麼處於一個帶 throws 宣告丟擲的方法中
  • throws 宣告丟擲異常有一個限制:方法重寫時,子類方法宣告丟擲的異常型別應該是父類方法宣告丟擲異常型別的子類或同類;子類方法宣告丟擲的異常型別不允許比父類方法宣告丟擲的異常多;

三、使用 throw 丟擲異常

1. 丟擲異常

  • 如果需要在程式中自行丟擲異常,則應使用 throw 語句;throw 語句丟擲的不是異常類,而是一個異常例項,而且每次只能丟擲一個異常例項
throw ExceptionInstance;
  • 如果 throw 語句丟擲的異常是 Checked 異常,則該 throw 語句要麼處於 try 塊裡,顯式捕獲該異常;要麼放在一個帶 throws 宣告丟擲的方法中;
  • 如果 throw 語句丟擲的異常是 Runtime 異常,則該語句無需放在 try 塊裡,也無需放在帶 throws 宣告丟擲的方法中;程式既可以顯式使用 try…catch 來捕獲並處理該異常,也可以完全不理會該異常,把該異常交給該方法呼叫者處理;
  • 自行丟擲 Runtime 異常比自行丟擲 Checked 異常的靈活性更好;
public class ThrowsTest2 {
    public static void main(String[] args) {
        try {
            //呼叫宣告丟擲的 Checked 異常方法
            //要麼顯式捕獲該異常,要麼在 main 中再次宣告
            throwCheckedTest(-3);
        }
        catch (Exception e){
            System.out.println(e.getMessage());
        }
        //呼叫宣告丟擲的 Runtime 異常方法
        //既可以顯式捕獲該異常,也可以不理會
        throwRuntimeTest(3);
    }

    public static void throwCheckedTest(int a) throws Exception{
        if (a > 0){
            //自行丟擲 Exception 異常
            //該程式碼必須處於 try...catch 塊,或者處於 throws 宣告的方法中
            throw new Exception("a大於0,不符合");
        }
    }

    public static void throwRuntimeTest(int a){
        if (a > 0){
            //自行丟擲 RuntimeException 異常
            throw new RuntimeException("a大於0,不符合");
        }
    }
}
//輸出結果如下:
Exception in thread "main" java.lang.RuntimeException: a大於0,不符合
	at ExceptionPackage.ThrowsTest2.throwRuntimeTest(ThrowsTest2.java:22)
	at ExceptionPackage.ThrowsTest2.main(ThrowsTest2.java:11)

2. 自定義異常類

3. catch 和 throw 同時使用

  • 在實際應用中需要更復雜的異常處理方式:當一個異常出現時,單靠某個方法無法完全處理該異常,必須由幾個方法協作才能完全處理該異常;
  • 可以在 catch 塊中結合 throw 語句來完成;

4. Java7 增強的 throw 語句

相關文章