Java Exception最佳實踐(轉)
https://www.dubby.cn/detail.html?id=9033
- 1.異常介紹
- 2.Java中的異常介紹
- 3.自定義異常
- 4.幾個建議
- 1)不要生吞異常
- 2)申明具體的異常
- 3)盡可能的捕獲具體異常
- 4)永遠不要捕獲Throwable
- 5)不要丟失異常信息
- 6)日誌和上拋不可兼得
- 7)不要在finally裏拋異常
- 8)不要為了捕獲而捕獲
- 9)不要使用printStackTrace()或類似的語句
- 10)不一定要catch
- 11)“Throw early catch late”
- 12)記得用finall善後
- 13)上拋信息明確的異常
- 14)永遠不要使用異常來做流程控制
- 15)盡早校驗輸入
- 16)一條信息打印異常
- 17)讓你的異常信息更充實
- 18)如果線程被interrupted一定要結束線程
- 19)使用模板來減少重復的try-catch代碼
- 20)文檔中加上異常說明
本篇文章主要給大家介紹一些眾所周知的異常處理原則,但是也有部分鮮為人知,但也很有用的原則,希望能引發各位對異常處理的思考,以及在開發過程中,寫出更優美的代碼。
1.異常介紹
大致可以把異常分成三種情況下的異常(不正常情況):
- 代碼錯誤引發的異常:比如數組越界,空指針等。
- 客戶端錯誤調用引發的異常:比如用戶名最長只允許32,客戶端傳了100;方法參數不能為空,客戶端傳了空等。
- 資源錯誤引發的異常:比如網絡錯誤,硬盤故障,文件被刪等。
2.Java中的異常介紹
無圖言屌,下面就給出異常的繼承關系圖:
主要說說這幾個概念:
- Checked exceptions:這種異常在代碼層面必須要捕獲或者在簽名處申明這個異常。這種異常是Java強制你必須捕獲,因為這些一般一般是不可避免的,比如:網絡,文件系統等不可控因素。
- Unchecked exceptions:這種異常不會強制捕獲或者在簽名處申明。這類異常一般是由於代碼問題產生的,比如:數組越界,空指針等。
- Errors:這類錯誤,一般是在軟件層面不可恢復的。比如:
OutOfMemoryError
,LinkageError
, 還有StackOverflowError
。這種錯誤一般會是的程序(或者程序的一部分)不可用。針對這類錯誤,一定要有一個良好的日誌習慣,不然很難定位。
3.自定義異常
一般我們想要自定義異常的目的都是為了讓異常信息更豐富,比如:輸入名稱不合法,我們可能希望有一個UsernameInvalidException
。本人在代碼中曾經這麽幹過,一個類似的用法,好處很明顯,在我們的內部監控系統中,對異常統計界面可以很清晰的反映出是什麽問題,但是也帶來一個問題,那就是異常數量很多。
在這裏我先擺幾個大師的意見吧:
- 不要使用自定義異常:
Java已經給我們提供了很多很多異常,盡量復用這些異常,好處有:減少我們的代碼量,也就減少了維護的成本和精力,不至於讓代碼中出現很多只用過一次或幾次的異常,最後異常數量爆炸(這就是我遇到的);使用通用異常,也可以減少別人閱讀我們的代碼,使用我們的接口時,更輕松,畢竟多一個類我們就需要理解這個類存在的意義。這裏給幾個經常可以用到的異常:IllegalStateException
UnsupportedOperationException
IllegalArgumentException
NoSuchElementException
NullPointerException
- 如果不得不自定義異常,那就寫個通用異常:
如果自己不得不寫的話,那就寫的詳細一下,不要只有個String來傳達信息,那完全可以用通用的異常來替代,給個包含詳細信息的例子:
public class OutOfRangeException
extends IllegalArgumentException {
private final long value, min, max;
public OutOfRangeException(long value, long min, long max) {
super("Value " + value + " out of range " +
"[" + min + ".." + max + "]");
this.value = value;
this.min = min;
this.max = max;
}
public long getValue() {
return value;
}
public long getMin() {
return min;
}
public long getMax() {
return max;
}
}
4.幾個建議
1)不要生吞異常
catch (NoSuchMethodException e) {
return null;
}
這樣做會讓這個異常信息永遠的丟失,你將無法知道這個異常的原因,怎麽去解決這個異常,甚至你不知道有這個異常的存在。
2)申明具體的異常
public void foo() throws Exception { //不正確的做法
}
這樣除了告訴調用方我可能會有異常之外沒有任何其他信息,而事實是我們本可以提供更具體的信息,建議這樣做:
public void foo() throws SpecificException1, SpecificException2 { //正確的做法
}
3)盡可能的捕獲具體異常
try {
someMethod();
} catch (Exception e) {
LOGGER.error("method has failed", e);
}
這麽做的問題是,如果你調用的方法中多了一個新的異常,他本來的目的是希望你處理這個新的異常,可是因為你在這裏捕獲了所有的異常,你可能會忽略這個提醒,而忘記捕獲。
4)永遠不要捕獲Throwable
這樣會捕獲本來不該有我們來處理的錯誤,包括一些我們的代碼無法處理的錯誤。
5)不要丟失異常信息
catch (NoSuchMethodException e) {
throw new MyServiceException("Some information"); //不正確
}
這樣做會完全丟失異常信息。
catch (NoSuchMethodException e) {
throw new MyServiceException("Some information: " + e.getMessage()); //不正確
}
這種做法會丟失堆棧信息,建議:
catch (NoSuchMethodException e) {
throw new MyServiceException("Some information: " , e); //正確
}
6)日誌和上拋不可兼得
catch (NoSuchMethodException e) {
LOGGER.error("Some information", e);
throw e;
}
這樣會導致一個問題,就是一個異常會有多份日誌,因為上層可能也會記一次日誌。所以要麽上拋,要麽記日誌,不要都做。
7)不要在finally裏拋異常
try {
someMethod(); //Throws exceptionOne
} finally {
cleanUp(); //If finally also threw any exception the exceptionOne will be lost forever
}
這樣的問題是,如果finally裏也拋異常,就會導致真正的異常信息丟失,你只會收到finally裏拋的異常。
8)不要為了捕獲而捕獲
catch (NoSuchMethodException e) {
throw e; //Avoid this as it doesn‘t help anything
}
這段代碼沒有任何有意義,你可以直接上拋。
9)不要使用printStackTrace()或類似的語句
這種輸出沒有任何意義,而且不確定輸出路徑,對定位問題沒有幫助。
10)不一定要catch
try {
someMethod(); //Method 2
} finally {
cleanUp(); //do cleanup here
}
如果你只是想要finally來做善後,那就只用它就可以了,不要用catch。
11)“Throw early catch late”
這句話我不想翻譯,因為我希望你能看到這句話,以後你也會見到這句話的。Throw early catch late。在底層拋異常,在信息足夠的時候來捕獲並處理。
12)記得用finall善後
比如數據庫連接,一定要用finally關閉連接。當然你也可以用try-with-resource的方式。
13)上拋信息明確的異常
如果這個方法是解析文件,那麽FileNotFoundException
就比NullPointException
更明確。
14)永遠不要使用異常來做流程控制
public void useExceptionsForFlowControl() {
try {
while (true) {
increaseCount();
}
} catch (MaximumCountReachedException ex) {
}
//Continue execution
}
public void increaseCount()
throws MaximumCountReachedException {
if (count >= 5000)
throw new MaximumCountReachedException();
}
算我求你了,不要這麽幹!
15)盡早校驗輸入
很多異常都是由不合法的輸入引起的,所以盡可能早的校驗輸入。
16)一條信息打印異常
LOGGER.debug("Using cache sector A");
LOGGER.debug("Using retry sector B");
何必呢?而且這樣也容易誤導其他人,建議:
LOGGER.debug("Using cache sector A, using retry sector B");
17)讓你的異常信息更充實
包括堆棧和其他提示信息。
18)如果線程被interrupted一定要結束線程
while (true) {
try {
Thread.sleep(100000);
} catch (InterruptedException e) {} //Don‘t do this
doSomethingCool();
}
這段代碼很cool,但是一般會interrupt線程,要麽是超時了,要麽是線程池被關閉了,所以你應該盡可能的去結束線程。
19)使用模板來減少重復的try-catch代碼
class DBUtil{
public static void closeConnection(Connection conn){
try{
conn.close();
} catch(Exception ex){
//Log Exception - Cannot close connection
}
}
}
使用這個來減少每次都try一遍。
20)文檔中加上異常說明
/**
* This method does something extremely useful ...
*
* @param input
* @throws MyBusinessException if ... happens
*/
public void doSomething(String input) throws MyBusinessException { ... }
前人種樹後人乘涼,你可能也可以乘涼。
Java Exception最佳實踐(轉)