這是我參與更文挑戰的第26天,活動詳情查看:更文挑戰java
異常處理
- Java類庫中定義的能夠經過預檢查方式規避的RuntimeException異常不該該經過catch方式來處理:
- NullPointerException
- IndexOutofBoundsException
- 沒法經過預檢查的異常除外: 在解析字符串形式數字時,不得不經過catch NumberFormatException來實現
if (obj != null) {}
複製代碼
- 異常不要用來作流程控制,條件控制:
- 異常設計的初衷是解決程序運行中的各類意外狀況,且異常的處理效率比條件判斷方式要低不少
- 使用catch時要區分穩定代碼和非穩定代碼:
- 穩定代碼: 不管如何不會出錯的代碼
- 非穩定代碼: 非穩定代碼的catch儘量區分異常類型,再作對應處理
- 對於大段代碼進行try - catch,會使得程序沒法根據不一樣的異常作出正確的應激反應,也不利於定位問題
- 在用戶註冊場景中,若是用戶輸入非法字符,或者用戶名稱已存在,或者用戶密碼過於簡單,在程序上做出分門別類的判斷,並提示給用戶
- 捕獲異常是爲了處理,不要捕獲了什麼都不處理.若是不須要處理,應該將異常拋給調用者
- 最外層的業務使用者,必須處理異常,將其轉化爲用戶能夠理解的內容
- 若是有try塊放到了事務代碼中 ,catch異常後,若是須要回滾事務,必定要注意手動回滾事務
- finally塊必須對資源對象,流對象進行關閉,有異常也要作try - catch
- JDK 7之後,可使用try - with - resources 方式
- 不要在finally塊中使用return:
- finally塊中的return返回後方法結束執行,不會再執行try塊中的return語句
- 捕獲異常與拋出異常必須徹底匹配,或者是拋異常的父類
- 方法的返回值能夠爲null,不強制返回空集合或者空對象等,必須添加註釋充分說明什麼狀況下會返回null值
- 即便調用方法返回空集合或者空對象,對於調用者來講,必須考慮到遠程調用失敗,序列化失敗,運行時異常等返回null的場景
- 必定要防止出現NPE異常,注意NPE產生的場景:
- 返回類型爲基本數據類型,return包裝數據類型的對象時, 自動拆箱有可能產生NPE
- 數據庫的查詢結果可能爲null
- 集合裏的元素即便isNotEmpty, 取出的數據元素也可能爲null
- 遠程調用返回對象時,一概要進行空指針判斷,防止NPE
- 對於Session中獲取的數據,建議進行NPE檢查,避免空指針
- 級聯調用obj.getA().getB.getC(), 一連串的調用,容易產生NPE
- JDK 8使用Optional類來防止NPE問題
- 定義時區分unchecked和checked異常,避免直接拋出new RuntimeException(), 不容許拋出Exception或者Throwable, 應該使用有業務含義的自定義異常
- 推薦使用業務界已定義過的異常:
- DAOException
- ServiceException
- 對於公司外的http或者api開放接口必須使用 "錯誤碼"; 應用內部推薦異常拋出; 跨應用間的RPC調用優先考慮使用Result方式,封裝isSuccess()方法,錯誤碼,錯誤簡短信息
- RPC方法使用Result方式的緣由:
- 使用拋異常返回方式,調用方若是沒有捕獲到就會產生運行時錯誤
- 若是不加棧信息,只是new自定義異常,加入本身理解的error message, 對於調用端解決問題的幫助不會太多.若是加了棧信息,在頻繁調用出錯的狀況下,數據序列化和傳輸的性能損耗也是問題
- 避免出現重複的代碼,即DRY(Don't Repeat Yourself)原則:
- 重複的代碼在之後的修改時,須要修改全部的副本,容易遺漏
- 抽取共性方法,或者抽象公共類,或者組件化
- 一個類中有多個public方法,都須要進行數行相同的參數校驗工做,這個時候就要進行抽取:
private boolean checkParam(DTO dto) {...}
複製代碼
日誌規約
- 應用中不可直接使用日誌系統(log4j,logback)中的API,應該使用日誌框架slf4j中的API, 使用門面模式的日誌框架,有利於維護和各個類的日誌處理方式統一
- 日誌文件至少保存15天,由於有些異常具有以 "周" 爲頻次發生的特色
- 應用中的擴展日誌(打點,臨時監控,訪問日誌等)命名方式:
- appName_logType_logName.log
- logType: 日誌類型,如 stats,monitor,access
- logName: 日誌描述
- 這樣經過文件名就能夠知道日誌文件屬於什麼應用,什麼類型,什麼目的,也方便歸類查找
- mppserver應用中單獨監控時區轉換異常: mppserver_monitor_timeZoneConvert.log
- 對日誌進行分類,好比將錯誤日誌和業務日誌分開存放,便於開發人員查看,也便於對日誌系統進行及時監控
- 對 trace,debug,info級別的日誌輸出,必須使用條件輸出形式或者使用佔位符方式
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
複製代碼
- 若是日誌級別是warn: 上述日誌不會打印,可是或執行字符串拼接操做
- 若是symbol是對象,會執行toString() 方法,浪費了系統資源,執行上述操做,最終日誌卻沒有打印
- 使用條件輸出形式:
if (logger.isDebugEnabled()) {
logger.debug("Processing trade with id: " + id + " and symbol: " + symbol);
}
複製代碼
logger.debug("Processing trade with id: {} and symbol: {}, id, symbol); 複製代碼
- 避免重複打印日誌,浪費磁盤空間,必須在log4j.xml中設置additivity=false
<logger name="com.oxford.dubbo.config" additivity="false">
複製代碼
- 異常信息包括:
- 案發現場信息
- 異常堆棧信息
- 若是不處理,應該經過異常關鍵字throws向上拋出
logger.error(各種參數或者對象toString() + "_" + e.getMessage(), e);
複製代碼
- 謹慎的記錄日誌:
- 生產環境禁止輸出debug日誌
- 有選擇地輸出info日誌
- 若是使用warn來記錄剛上線時的業務行爲信息,必定要注意日誌輸出量問題,避免服務器內容過多,並及時刪除這些觀察日誌
- 大量地輸出無效日誌,不利於系統性能的提高,也不利於快速定位錯誤點
- 記錄日誌時須要思考:
- 這些日誌真的有人看嗎?
- 看到這條日誌可以作什麼?
- 能不能給排查問題帶來好處?
- 可使用warn日誌級別來記錄用戶輸入參數錯誤的狀況
- 注意日誌的輸出級別:
- error級別只記錄系統邏輯出錯,異常或者重要的錯誤信息
- 使用全英文來註釋和描述日誌錯誤信息