異常處理全面解析

N1ce2cu發表於2024-07-13

checked和unchecked異常

  • checked 異常(檢查型異常)在原始碼裡必須顯式地捕獲或者丟擲,否則編譯器會提示你進行相應的操作;而 unchecked 異常(非檢查型異常)就是所謂的執行時異常,通常是可以透過編碼進行規避的,並不需要顯式地捕獲或者丟擲。

  • NoClassDefFoundError:程式在編譯時可以找到所依賴的類,但是在執行時找不到指定的類檔案,導致丟擲該錯誤;原因可能是 jar 包缺失或者呼叫了初始化失敗的類。

  • ClassNotFoundException:當動態載入 Class 物件的時候找不到對應的類時丟擲該異常;原因可能是要載入的類不存在或者類名寫錯了。

throw和throws

1)throws關鍵字用於宣告異常,它的作用和try-catch相似;而throw關鍵字用於顯式的丟擲異常。

throws ArithmeticException;

2)throws關鍵字後面跟的是異常的名字;而throw關鍵字後面跟的是異常的物件。

throw new ArithmeticException("算術異常");

3)throws關鍵字出現在方法簽名上,而throw關鍵字出現在方法體裡。

4)throws關鍵字在宣告異常的時候可以跟多個,用逗號隔開;而throw關鍵字每次只能丟擲一個異常。

  • 如果有好幾個類似的方法都可能出現異常,如果為每個方法都加上try-catch,就會顯得非常繁瑣。一個解決辦法就是,使用throws關鍵字,在方法簽名上宣告可能會丟擲的異常,然後在呼叫該方法的地方使用try-catch進行處理。
public static void main(String args[]){
    try {
        myMethod1();
    } catch (ArithmeticException e) {
        // 算術異常
    } catch (NullPointerException e) {
        // 空指標異常
    }
}
public static void myMethod1() throws ArithmeticException, NullPointerException{
    // 方法簽名上宣告異常
}

try-catch-finally

  • 一個 try 塊後面可以跟多個 catch 塊,用來捕獲不同型別的異常並做相應的處理,當 try 塊中的某一行程式碼發生異常時,之後的程式碼就不再執行,而是會跳轉到異常對應的 catch 塊中執行。

  • 如果一個 try 塊後面跟了多個與之關聯的 catch 塊,那麼應該把特定的異常放在前面,通用型的異常放在後面,不然編譯器會提示錯誤。

  • finally 塊前面必須有 try 塊,不要把 finally 塊單獨拉出來使用。編譯器也不允許這樣做。

  • finally 塊不是必選項,有 try 塊的時候不一定要有 finally 塊。

  • 如果 finally 塊中的程式碼可能會發生異常,也應該使用 try-catch 進行包裹。

  • 即便是 try 塊中執行了 return、break、continue 這些跳轉語句,finally 塊也會被執行

  • 不執行finally的情況:遇到了死迴圈;執行了 System. exit() 這行程式碼。

class MyFinallyReadLineThrow {
    public void close() throws Exception {
        throw new Exception("close");
    }

    public void readLine() throws Exception {
        throw new Exception("readLine");
    }
}

public class Test {
    public static void main(String[] args) throws Exception {
        MyFinallyReadLineThrow myThrow = null;
        try {
            myThrow = new MyFinallyReadLineThrow();
            // readLine的異常資訊丟失
            myThrow.readLine();
        } finally {
            myThrow.close();
        }
        /*
        Exception in thread "main" java.lang.Exception: close
            at test.ExceptionTest.MyFinallyReadLineThrow.close(Test.java:5)
            at test.ExceptionTest.Test.main(Test.java:21)
         */
    }
}

try-with-resources

  • 當一個異常被丟擲的時候,可能有其他異常因為該異常而被抑制住,從而無法正常丟擲。這時可以透過 addSuppressed() 方法把這些被抑制的方法記錄下來,然後被抑制的異常就會出現在丟擲的異常的堆疊資訊中,可以透過 getSuppressed() 方法來獲取這些異常。這樣做的好處是不會丟失任何異常,方便我們進行除錯。
package test.ExceptionTest;

class MyFinallyReadLineThrow implements AutoCloseable {
    @Override
    public void close() throws Exception {
        throw new Exception("close");
    }

    public void readLine() throws Exception {
        throw new Exception("readLine");
    }
}


public class Test {
    public static void main(String[] args) {
        try (MyFinallyReadLineThrow myThrow = new MyFinallyReadLineThrow()) {
            myThrow.readLine();
        } catch (Exception e) {
            e.printStackTrace();
        }
        /*
        java.lang.Exception: readLine
            at test.ExceptionTest.MyFinallyReadLineThrow.readLine(Test.java:10)
            at test.ExceptionTest.Test.main(Test.java:18)
            Suppressed: java.lang.Exception: close
               at test.ExceptionTest.MyFinallyReadLineThrow.close(Test.java:6)
               at test.ExceptionTest.Test.main(Test.java:19)
         */
    }
}

相關文章