重學Java - 異常處理

CPPRTO發表於2020-11-21

引言

本篇博文為 Java 異常處理的常見概念及相關細節梳理,意在重學 Java 查漏補缺。
博文隨時會進行更新,補充新的內容並修正錯漏,該系列博文旨在幫助自己鞏固紮實 Java 基礎。
畢竟萬丈高樓,基礎為重,藉此督促自己時常溫習回顧。

一、異常概述

異常: 在 Java 語言中,將程式執行中發生的不正常情況稱為 “異常”。(開發過程中的語法錯誤和邏輯錯誤不是異常)

Java 程式在執行過程中所發生的異常事件可分為兩類:

  • Error:Java 虛擬機器無法解決的嚴重問題。
    • 如:JVM 系統內部錯誤、資源耗盡等嚴重情況。
    • 比如:StackOverflowError(棧溢位) 和 OOM(OutOfMemoryError 堆溢位)
    • 一般不編寫針對性的程式碼進行處理
  • Exception:其他因程式設計錯誤或偶然的外在因素導致的一般性問題,可以使用針對性的程式碼進行處理,例如:
    • 空指標訪問
    • 試圖讀取不存在的檔案
    • 網路連線中斷
    • 陣列下標(索引)越界

對於這些錯誤,一般有兩種解決方法:

  • 遇到錯誤就終止程式的執行
  • 在編寫程式時,考慮到錯誤的檢測、錯誤訊息的提示以及錯誤的處理

二、異常分類

捕獲錯誤最理想的是在編譯期間,但有些錯誤只有在執行時才會發生,比如:

  • 除數為零(一個數除以 0)
  • 陣列下標越界

分類為: 編譯時異常、執行時異常

2.1、編譯時異常(受檢異常 checked)

  • Throwable
    • Exception
      • IOException
        • EOFException
        • FileNotFoundException
        • MalformedURLException
        • UnknownHostException
      • ClassNotFoundException
      • CloneNotSupportedException

2.2、執行時異常(非受檢異常 unchecked)

Error

  • RuntiomException
    • ArithmeticException
    • ClassCastException
    • IllegalArgumentException
    • IllegalStateException
    • IndexOutOfBoundsException
    • NoSuchElementException
    • NullPointerException

2.3、常見異常

java.lang.Throwable
	|----java.lang.Error: 一般不編寫針對性的程式碼進行處理
	|----java.lang.Exception: 可以進行異常的處理
		|----編譯時異常(checked)
			|----IOException
				|----FileNotFoundException
            |----ClassNotFoundException
        |----執行時異常(unchecked)
        	|----NullPointerException(空指標異常:引用變數指向 null 使用引用變數時觸發)
        	|----ArrayIndexOutOfBoundsException(陣列下標越界: array[array.length])
        	|----StringIndexOutOfBoundsException(String 下標越界)
        	|----ClassCastException(型別轉換異常: Object obj = new Date();String str = (String)obj;)
        	|----NumberFormatException(數值格式化異常: String str = "a";int num = Integer.parseInt(str);)
        	|----InputMismatchException(輸入不匹配異常:Scanner input = Scanner(System.in);input.nextInt();此時鍵盤鍵入 a 觸發)
        	|----ArithmeticException(算數異常: int result = 1/0;)

三、異常處理

異常的處理:“抓拋模型”

  • “拋”:程式在正常執行的過程中,一旦出現異常,就會在異常程式碼處生成一個對應異常類的物件,並將此物件丟擲。
    一旦丟擲物件後,其後的程式碼就不再繼續執行
  • “抓”:可以理解為異常的處理方式
    • try-catch-finally
    • throws

Java 程式的執行過程中如出現異常,會生成一個異常類物件,該異常類物件將被交給 Java 執行時系統,這個過程稱為丟擲(throw)異常

異常物件的生成:

  • 由虛擬機器自動生成:程式執行過程中,虛擬機器檢測到程式發生了問題,如果在當前程式碼中沒有找到相應的處理程式,就會在後臺自動建立一個對應異常類的例項物件並丟擲 —— 自動丟擲
  • 由開發人員手動建立:Exception exception = new ClassCastException(); —— 建立好的異常物件不丟擲對程式沒有任何影響,和建立一個普通物件一樣

3.1、try-catch-finally

格式:

try {
    // 可能出現異常的程式碼
} catch(異常型別1 變數名1) {
    // 處理異常的方式1
} catch(異常型別2 變數名2) {
    // 處理異常的方式2
}
...
finally {
    // 一定會執行的程式碼
}

說明:

  • finally 是可選的
  • 使用 try 將可能出現異常的程式碼包裝起來,在執行過程中,一旦出現異常,就會生成一個異常類的物件,根據此物件的型別,去 catch 中進行匹配
  • 一旦 try 中的異常物件匹配到某一個 catch 時,就進入 catch 中進行異常的處理。一旦處理完成就跳出當前的 try-catch 結構(在沒有 finally 的情況)。繼續執行其後的程式碼
  • catch 中的異常型別如果沒有子、父類關係,則宣告順序隨意
    catch 中的異常型別如果滿足子、父類關係,則要求子類一定宣告在父類之前,否則編譯報錯
  • 常用的異常物件處理的方式:
    • String getMessage():列印異常資訊
    • printStackTrace():列印堆疊
  • 在 try 結構中宣告的變數,在出了 try 結構後不能再被呼叫
  • try-catch-finally 結構可以巢狀

注意: 使用 try-catch-finally 處理編譯時異常,使得程式在編譯時就不再報錯,但是執行時仍可能報錯。相當於使用 try-catch-finally 將一個編譯時可能出現的異常延遲到執行時出現

3.2、throws + 異常型別

  • “throws + 異常型別” 寫在方法的宣告處。指明此方法執行時,可能會丟擲的異常型別
    一旦方法執行時出現異常,仍會在異常程式碼處生成一個異常類物件,此物件滿足 throws 後的異常型別時,就會被丟擲。異常程式碼後續的程式碼不再繼續執行
  • try-catch-finally 方式真正的將異常進行了處理
    throws 方式只是將異常拋給了方法的呼叫者,並沒有真正將異常進行處理

3.3、try-catch-finally 與 throws 的使用場景

開發中根據不同場景選用不同的處理方式

  • 如果父類中被重寫的方法沒有 throws 方式處理異常,則子類重寫的方法也不能使用 throws,意味著如果子類重寫的方法中若涉及異常,必須使用 try-catch-finally 方式處理
  • 要執行的方法中,先後又呼叫了另外的幾個方法,這幾個方法是通過遞進關係執行的,這時建議這幾個方法使用 throws 方式進行處理;而這個要執行的方法可以考慮使用 try-catch-finally 方式進行處理

3.4、手動丟擲異常

格式: throw new 異常類構造器(引數);

throw 和 throws 的區別:

  • throw 表示丟擲一個異常類的物件,生成異常類的過程。生命在方法體內
  • throws 屬於異常處理的一種方式,宣告在方法的宣告處

3.5、自定義異常類

自定義異常的方式:

  • 繼承於現有的異常類:RuntimeException、Exception
  • 提供全域性常量:serialVersionUID:(唯一標識)
  • 提供過載的構造器