什麼是異常
在理想情況下,程式總會運行在很完美的環境中,網路不會終端,檔案一定存在,程式不會有 BUG。但是,理想很豐滿,現實很骨幹,實際生產環境中,網路可能會中斷,檔案可能會找不到,記憶體可能會溢出,程式可能會有 BUG。而這些意料之外的情況就是異常。
在未處理的情況下,異常會導致程式無法繼續執行,從而影響軟體整體的功能,但這是多數情況下不允許的,所以我們需要在程式中將可處理的異常處理掉,至少保證當前任務可以安全退出。
Java 異常
Java 異常體系中,Throwable 為超類,其子類包括 Error 和 Exception 兩類,Error 主要包括 JVM 本身的問題,這一類問題很少出現且無法被程式所解決,所以代碼中可以不考慮此部分。
Java 異常體系將異常分為檢查異常(checked exception)和未檢查異常(unchecked exception)。
未檢查異常也可叫做運行時異常,因為其在 Java 異常體系中僅包含 RuntimeException,主要包括代碼編寫時人為因為做成的異常,而這些異常在代碼編譯階段無法發現,常見的有 IndexOutOfBoundsException、NullPointerException 和 IndexOutOfBoundsException 等,當出現未檢查異常時可確定是代碼 BUG。
檢查異常包括除 RuntimeException 以外的所有異常,常見的主要為 IOException 和 SQLExcetion 兩類,此部分異常雖然可能為人為造成,但在代碼編譯階段就可以被編譯器發現,所以需要在代碼中做好相應的處理。
Java 異常處理
對應異常體系,Java 提供了異常處理機制,常用的關鍵字有 try、catch、finally、throw 和 throws。
try、catch 和 finally 的用法比較簡單,只需要符合標準文法即可,try 對代碼塊進行監聽,當出現異常時 catch 捕獲異常進行異常處理,最後執行 finally 代碼塊。需要注意幾個點:
- 是無論是否出現異常,finally 代碼塊是一定執行,且在最後執行
- catch 可以出現多次,但出現多次時需要注意異常的父子級關係,後面的異常不能為前面的子類
try{ // 代碼塊} catch (異常){ // 異常處理代碼} finally { // 需要確保一定執行的代碼,例如關閉流資源佔用的代碼}
throw 和 throws 比較容易混淆。throw 用於在代碼中手動拋出異常,而 throws 則用於在方法簽名後聲明需要拋出的異常,涉及到繼承關係時,子類方法拋出異常時,父類方法必須拋出異常,不能為子類異常的子類且數量不能少於子類,代碼結構如下:
public void testMethod() throw Exception{ throw new RuntimeException();}
Java 異常的使用建議
- 僅當有需要或者能處理時對異常進行捕獲,否則將異常拋向調用者,直至到與頁面進行互動時統一處理或記錄日誌
- finally 代碼塊建議只用來釋放佔用的資源,不要用來處理商務邏輯
- 不要通過異常處理機制處理 RuntimeException,因為此類異常可以通過修改代碼來解決,同時增加程式的健壯性
- 異常處理記錄日誌時記錄對問題排查有用的資訊,無法確定時則將異常堆棧資訊及方法運行參數記錄下來
- catch 捕獲異常時粒度儘可能,例如捕獲 FileNotFoundException 時不要用 IOException 代替
- try 監聽的代碼儘可能少,不要將整個方法體都放在 try 語句中