Java 中的Exception 有什麼用?

ObjectiveSql發表於2020-10-26

Exception 的作用,這個問題是一個開放性的問題,沒有標準的答案,不同經歷的人可能會有不同的答案,也就是說經歷過慘痛的經歷後,才會對Java Exception 有深刻的認識,單純理論學習,按部就班的按所謂的規範使用,無法準確的表達出Java Exception 的內在邏輯。

Exception 含義

Exception,顧名思義,代表著程式執行的過程中出現一種不正常的狀態,需要中止程式的持續執行,同時又能快速的發現程式為什麼會出現錯,通過異常的資訊能夠快速定位,所以異常需要提供錯誤的上下文的狀態資訊,尤其是應用系統自身丟擲的異常,需要將上下文狀態儘可能多的輸出,有助於排查和定位。Java 中的NullPointerException 對外提供的資訊極少,所以應用系統應該儘可能catch 這類異常,包裝為資訊量更加豐富的異常丟擲。

Exception 也是一種程式邏輯控制的方式,是程式健壯性的一種表現。其實,大都數異常,程式都應該有一定的相應的處理邏輯,例如,ArrayIndexOutOfBoundsException,這類異常在編碼過程中應該能夠被預見,並需要做出現相應的邏輯控制,有經驗的程式設計師,在編碼的過程中就已經考慮到各種異常的情況,待整個系統上線後,出現的問題相對較少,而普通的程式設計師往往只保證程式在Happy Case 時能夠正常執行,卻忽略了各種異常情況,往往是在出現問題後進行補救,導致程式在反覆補救後,邏輯混亂,這也就是很多專案早期的程式碼質量高,經過一段時間後,程式碼慘不忍睹的根源。

Exception 的處理方式

什麼時候需要catch Exception?什麼時候需要throw 呢?這也是不會有標準答案的過程,Java 本身也沒有給出標準答案,也沒有指導原則。但經過無數次實踐的經驗後,你就能體會對異常的不同處理方式,對系統邏輯的穩定性性有著截然不同的影響,往往出現很詭異的Bug時,同時可參考的日誌或其它關鍵丟失,導致故障不能及時解決,才會發現關鍵的位置異常被人為的隱藏是多麼愚蠢的行為。

我在使用一個開源的框架時,時常會發現重要的異常資訊被隱藏,導致花費大量的時間去跟蹤程式碼。異常在什麼時候需要throw,什麼時候需要catch,原則其實很簡單:

  1. 異常在能夠完全掌控的情況下,而且也有明確的邏輯處理時,需要catch,並進行相應的處理。同時,也需要關注外部程式是否需要感知異常,如果需要,則封裝新的異常後重新throw;
  2. 無法決定處理邏輯的情況下,需要將所有異常throw,讓外部程式決定處理邏輯。同時,針對內部的資訊含量較少的Runtime 型的異常,需要額外封裝,避免在同一行程式碼中,無法確定哪一個物件為null。

Exception 定義

什麼時候定義Exception 同樣是沒有任何指導原則,不同的技術框架中對異常定義的方式和方法都不太一樣,畢竟每個應用系統的邏輯結構和複雜度都不太一定,我根據自身的經驗整理了兩個指導原則:

  1. 程式需要中止,自身又無法完整的處理邏輯轉折,只能由外部呼叫者根據異常的資訊,作為合理的邏輯處理,例如:資料採集時,出現了非法的資料或字串,採集程式是無法判斷邏輯轉折後,就如何處理,同時又需要明確區分非常資料,甚至需要定義出錯誤的型別,由外部程式決定是忽略還是終止執行,在這種情況下需要定義明確的異常,用於標識錯誤的型別。
  2. 內部程式處理過程中出現太多異常,並且這類異常具有共同的特性(例如:ClassNotFountException, NoSuchMehtodException 等),不需要外部程式對這類異常分別處理時,需要定義一個異常用於包裝內部異常,提升程式碼的可讀性。
  3. 錯誤資訊的補充,例如上文所提到的NullPointerException,應用系統程式需要為此異常補充更完整的資訊,便於外部程式或日誌系統提供足夠的資訊,便於故障的解決;

Exception 和 RuntimeException 有什麼區別,在什麼場景下定義,也是一個仁者見仁,智者見智的問題,Java 沒有任何指導建議,按我個人的判斷,JDK中也有很多的定義也不是特別合理,例如:IndexOutOfBoundsException 應該是一個Exception, JDK 卻定義為RuntimeExcption 這類錯誤需要明確的提醒外部程式對陣列進行邏輯限制,很多應用系統在總是在出現錯誤後,才能主動Cache 這類異常;然而像 ClassNotFoundException 這類異常在極少數狀態下才會進行邏輯處理JDK 卻定義為Exception 導致很多反射類的專案的處理邏輯變得複雜,NullPointerException 也是經常出現的,但定義為RuntimeException 是合理的,可能因為JDK 程式從邏輯上根本無法捕捉NullPointerException,應該是在JVM 執行過程中才能夠進行邏輯判斷,具體並未研究。

經過上面的兩個示例也很容易看出Exception 和RuntimeException 有什麼區別,JDK 的設計應該從外部程式使用的角度進行異常設計,由於外部程式導致的異常,並且外部程式應當有邏輯處理異常狀態,這類異常理應定義為Exception,而其它由於JVM 在編碼期無法判斷,也無法從語法層面提供解釋的Exception 應該定義為RuntimeException。

結論

針對Java 異常的解讀是我個人的見解,就像古詩詞一樣,不同人有不同的解讀,相信JDK 的設計者也無法給 Java Exception 一個明確的解釋和原則,只能靠歷史經驗的積累和不斷的錯誤中,逐形成相對完整的理論,供後人學習。

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章