異常規(guī)范
異常介紹
Throwable
所有Exception和Error的父類(lèi).
Error
致命錯(cuò)誤. 項(xiàng)目自身存在問(wèn)題, 諸如格式有問(wèn)題, 編譯版本不對(duì), 堆棧溢出等, 項(xiàng)目在出現(xiàn)ERROR的情況下是不應(yīng)該運(yùn)行的. 同時(shí), 程序遇到Error時(shí), 程序不需要, 通常也是沒(méi)有能力做處理的, 只能夠停止程序針對(duì)項(xiàng)目或者運(yùn)行環(huán)境做人工處理才行.
Exception
區(qū)別于Error, 是程序可以自己處理的異常. Exception的子類(lèi)中, 特殊的RuntimeException被稱(chēng)為運(yùn)行時(shí)異常, 也叫非受檢異常; 其他的子類(lèi)包括Exception類(lèi)本身都叫受檢異常
受檢異常
Java編譯器會(huì)檢查它,也就是說(shuō),當(dāng)程序中可能出現(xiàn)這類(lèi)異常,要么用try-catch語(yǔ)句捕獲它,要么用throws子句聲明拋出它,否則編譯不會(huì)通過(guò)。
非受檢異常(運(yùn)行時(shí)異常)
不需要強(qiáng)制catch或者throw的異常.
程序中如何使用異常
程序中我們主要關(guān)注受檢異常和運(yùn)行時(shí)異常的使用
一些原則, 這些原則并不獨(dú)立, 互相之間有照應(yīng)或者補(bǔ)充:
發(fā)生可恢復(fù)錯(cuò)誤的拋出受檢異常,程序錯(cuò)誤就拋出運(yùn)行時(shí)異常
-
盡量使用運(yùn)行時(shí)異常. 從保障代碼簡(jiǎn)潔, 清晰, 有意義的角度上來(lái)說(shuō).
注意絕對(duì)不是無(wú)腦把受檢異常換為運(yùn)行時(shí)異常.
很多時(shí)候我們要延遲處理異常: 比如我們的一個(gè)受檢異常在層次很深的地方拋出, 但是我們?cè)诖a層次很高的地方才能做處理, 那么受檢異常會(huì)出現(xiàn)在代碼調(diào)用的每一層. 這非常繁瑣, 也不清晰.
-
謹(jǐn)慎拋出受檢異常.
受檢異常是不受歡迎的.
除非你認(rèn)為你是在強(qiáng)調(diào)這個(gè)異常, 調(diào)用者在大多數(shù)情況下需要重點(diǎn)關(guān)注這個(gè)異常并catch這個(gè)異常并做處理.
使用運(yùn)行時(shí)異常帶來(lái)的簡(jiǎn)潔并不能夠彌補(bǔ)開(kāi)發(fā)人員忽略了這個(gè)異常帶來(lái)的問(wèn)題時(shí).
-
作為定位是類(lèi)庫(kù)的模塊, 盡量使用運(yùn)行時(shí)異常, 并對(duì)java低層異常封裝, 拋出類(lèi)庫(kù)特有的概括性的異常.
當(dāng)站在調(diào)用者的角度, 可以獲悉這個(gè)類(lèi)庫(kù)有哪幾種異常, 出現(xiàn)時(shí)代表什么了.
移位類(lèi)庫(kù)的調(diào)用很多時(shí)候跟業(yè)務(wù)沒(méi)有關(guān)系, 當(dāng)出現(xiàn)錯(cuò)誤時(shí), 通常是因?yàn)槲覀兊拇a漏洞造成的, 這并不能簡(jiǎn)單通過(guò)try_catch進(jìn)行恢復(fù), 所以盡量不使用受檢異常.
-
作為定位是服務(wù)的模塊, 可以使用一些受檢異常.
因?yàn)楫?dāng)調(diào)用服務(wù)出現(xiàn)錯(cuò)誤, 一般是一個(gè)可以解釋的業(yè)務(wù)錯(cuò)誤, 如果是想要調(diào)用者非常注意的錯(cuò)誤, 可以使用受檢異常.
服務(wù)的調(diào)用一般代碼層次比較淺, 并且是和業(yè)務(wù)比較相關(guān)的.
-
業(yè)務(wù)異常需要單獨(dú)封裝成新的異常來(lái)表達(dá)一類(lèi)或者一個(gè)模塊的業(yè)務(wù)錯(cuò)誤, 可以使用受檢異常. 但也參照1, 2, 3
可以把一些非業(yè)務(wù)異常封裝成為業(yè)務(wù)異常, 如果你知道在這個(gè)地方這種非業(yè)務(wù)異常在業(yè)務(wù)上可以表達(dá)一些含義.
比如某個(gè)位置拋出了json解析異常, 我們可以說(shuō)傳入的某個(gè)數(shù)據(jù)格式是錯(cuò)誤的.
為了給大家建立異常體系結(jié)構(gòu), 業(yè)務(wù)異常定義為受檢異常, 強(qiáng)制讓大家關(guān)注下.
-
非業(yè)務(wù)異常, 代碼底層異常, 如果出現(xiàn)的話可以定義為代碼bug的, 使用運(yùn)行時(shí)異常
即使沒(méi)有catch住的后果是在系統(tǒng)運(yùn)行時(shí)拋給了用戶(hù), 也不應(yīng)該catch. 當(dāng)然在項(xiàng)目中需要一個(gè)最高層次的異常處理, 對(duì)非業(yè)務(wù)異常統(tǒng)一catch記錄報(bào)警而不要暴露給用戶(hù)
-
業(yè)務(wù)異常如果可以, 不要跨層(跨模塊)
比如
controller -> service -> adaptor -> RPC
PRC 拋出的異常, 應(yīng)該在adaptor或者service做處理封裝新的異常, 不要讓controller直面RPC的異常.
-
異常應(yīng)該攜帶更多信息.
尤其對(duì)業(yè)務(wù)異常來(lái)說(shuō), 知道異常發(fā)生時(shí)的業(yè)務(wù)數(shù)據(jù)是很重要的, 方便查找定位問(wèn)題.
在api層(controller層), 將一些業(yè)務(wù)異常封裝為API異常, 這類(lèi)異常將直接給用戶(hù)api異常的提示, 且有時(shí)可以認(rèn)為這些異常是正常的, 不需要報(bào)警的.
-
有效的業(yè)務(wù)異常類(lèi)劃分和異常code定義, 有助于統(tǒng)一處理異常時(shí)區(qū)別異常的等級(jí)合適否需要報(bào)警.
在設(shè)計(jì)異常時(shí)請(qǐng)考慮這一點(diǎn).
如果不知道自己的異常應(yīng)該是使用受檢異常還是運(yùn)行時(shí)異常, 使用運(yùn)行時(shí)異常.
先報(bào)出錯(cuò)誤, 不做對(duì)未知的設(shè)計(jì)
如何處理異常
- 絕對(duì)禁止catch后什么都不做!
- 在catch之后封裝成新異常拋出的時(shí)候, 不要記錄日志. 因?yàn)槟銙伋隽? 會(huì)有上層來(lái)處理記錄日志, 只要沒(méi)有1這種情況, 總會(huì)有信息的. 這里再記錄日志就重復(fù)了.
- 在需要時(shí)一定要使用上finally
- 處理異常時(shí)記錄的日志一般要把異常的堆棧給記錄下來(lái).