阿里巴巴對Java程式設計【異常處理】的規約
異常處理
1. 【強制】 Java 類庫中定義的一類 RuntimeException 可以通過預先檢查進行規避,而不應該通過 catch 來處理,比如: IndexOutOfBoundsException , NullPointerException 等等。
說明:無法通過預檢查的異常除外,如在解析一個外部傳來的字串形式數字時,通過 catchNumberFormatException 來實現。
正例: if (obj != null) {...}
反例: try { obj.method() } catch (NullPointerException e) {...}
2. 【強制】異常不要用來做流程控制,條件控制,因為異常的處理效率比條件分支低。
3. 【強制】對大段程式碼進行 try - catch ,這是不負責任的表現。 catch 時請分清穩定程式碼和非穩定程式碼,穩定程式碼指的是無論如何不會出錯的程式碼。對於非穩定程式碼的 catch 儘可能進行區分異常型別,再做對應的異常處理。
4. 【強制】捕獲異常是為了處理它,不要捕獲了卻什麼都不處理而拋棄之,如果不想處理它,請將該異常拋給它的呼叫者。最外層的業務使用者,必須處理異常,將其轉化為使用者可以理解的內容。
5. 【強制】有 try 塊放到了事務程式碼中, catch 異常後,如果需要回滾事務,一定要注意手動回滾事務。
6. 【強制】 finally 塊必須對資源物件、流物件進行關閉,有異常也要做 try - catch 。
說明:如果 JDK 7 及以上,可以使用 try - with - resources 方式。
7. 【強制】不能在 finally 塊中使用 return , finally 塊中的 return 返回後方法結束執行,不會再執行 try 塊中的 return 語句。
8. 【強制】捕獲異常與拋異常,必須是完全匹配,或者捕獲異常是拋異常的父類。
說明:如果預期對方拋的是繡球,實際接到的是鉛球,就會產生意外情況。
9. 【推薦】方法的返回值可以為 null ,不強制返回空集合,或者空物件等,必須添加註釋充分說明什麼情況下會返回 null 值。呼叫方需要進行 null 判斷防止 NPE 問題。
說明:本手冊明確防止 NPE 是呼叫者的責任。即使被呼叫方法返回空集合或者空物件,對呼叫者來說,也並非高枕無憂,必須考慮到遠端呼叫失敗、序列化失敗、執行時異常等場景返回null 的情況。
10. 【推薦】防止 NPE ,是程式設計師的基本修養,注意 NPE 產生的場景:
1 ) 返回型別為基本資料型別,return 包裝資料型別的物件時,自動拆箱有可能產生 NPE。
反例: public int f() { return Integer 物件}, 如果為 null ,自動解箱拋 NPE 。
2 ) 資料庫的查詢結果可能為 null 。
3 ) 集合裡的元素即使 isNotEmpty ,取出的資料元素也可能為 null 。
4 ) 遠端呼叫返回物件時,一律要求進行空指標判斷,防止 NPE 。
5 ) 對於 Session 中獲取的資料,建議 NPE 檢查,避免空指標。
6 ) 級聯呼叫 obj . getA() . getB() . getC(); 一連串呼叫,易產生 NPE 。
正例:使用 JDK8 的 Optional 類來防止 NPE 問題。
11. 【推薦】定義時區分 unchecked / checked 異常,避免直接丟擲 new RuntimeException() ,更不允許丟擲 Exception 或者 Throwable ,應使用有業務含義的自定義異常。推薦業界已定義過的自定義異常,如: DAOException / ServiceException 等。
12. 【參考】在程式碼中使用“拋異常”還是“返回錯誤碼”,對於公司外的 http / api 開放介面必須使用“錯誤碼” ; 而應用內部推薦異常丟擲 ; 跨應用間 RPC 呼叫優先考慮使用 Result 方式,封裝 isSuccess()方法 、“錯誤碼”、“錯誤簡簡訊息”。
說明:關於 RPC 方法返回方式使用 Result 方式的理由:
1 ) 使用拋異常返回方式,呼叫方如果沒有捕獲到就會產生執行時錯誤。
2 ) 如果不加棧資訊,只是 new 自定義異常,加入自己的理解的 error message ,對於呼叫端解決問題的幫助不會太多。如果加了棧資訊,在頻繁調用出錯的情況下,資料序列化和傳輸的效能損耗也是問題。
13. 【參考】避免出現重複的程式碼 (Don ’ t Repeat Yourself) ,即 DRY 原則。
說明:隨意複製和貼上程式碼,必然會導致程式碼的重複,在以後需要修改時,需要修改所有的副本,容易遺漏。必要時抽取共性方法,或者抽象公共類,甚至是元件化。
正例:一個類中有多個 public 方法,都需要進行數行相同的引數校驗操作,這個時候請抽取:
private boolean checkParam(DTO dto) {...}