什么時(shí)候拋出異常(throw)
1、驗(yàn)證-拋出:當(dāng)程序檢測(cè)到非預(yù)期狀態(tài)、當(dāng)前處理層無法處理的,應(yīng)創(chuàng)建一個(gè)新的異常對(duì)象,并使用關(guān)鍵字throw,將異常拋給調(diào)用者
if (somethingIsInvalid) {
throw new SomeException("msg");
}
2、檢測(cè)-上拋:當(dāng)調(diào)用了一個(gè)聲明受檢異常的方法,當(dāng)前處理層無法處理,需要進(jìn)行適當(dāng)包裝的,應(yīng)創(chuàng)建一個(gè)異常封裝它,并使用關(guān)鍵字throw,將異常拋給調(diào)用者
} catch (SomeException se) {
throw new MethodDeclaredException(se);
}
3、re-throw: 當(dāng)調(diào)用了的方法拋出異常時(shí)(受檢或非受檢),根據(jù)情況可catch后進(jìn)行一定的處理,然后重新throw出去(或封裝一下再throw)
} catch (SomeException se) {
doSomethingHere();
throw se;
}
什么時(shí)候聲明異常(throws)
1、受檢異常必須聲明,非受檢異常可不必聲明
public void doSomething() throws ResourceFailureException {
2、目的:明確告知調(diào)用者可能拋出哪些異常;JavaDoc也可對(duì)該異常進(jìn)行說明使API更加友善
finally的使用
使用一個(gè)底層資源時(shí),完成任務(wù)后必須主動(dòng)關(guān)閉它。關(guān)閉的目的可能是釋放計(jì)算機(jī)某種設(shè)備、網(wǎng)絡(luò)、也可能是改變資源的狀態(tài)將之歸還給管理者等。
必須做的事情,在try-finally塊的finally中完成,每關(guān)閉一個(gè)資源應(yīng)單獨(dú)再做一次try-catch:
Resource resource = null;
try {
resource = loadResource();
return doSomething(resource);
} finally {
if (resource != null) {
try {
resource.close();
} catch (Exception ex) {
logger.error("", ex);
}
}
}
什么時(shí)候catch異常
使用
try {
execute();
} catch (SomeExceptin se) {
handlerSomeException(se);
}
可以保證當(dāng)try中的語句發(fā)生SomeException異常的時(shí)候能夠執(zhí)行 handlerSomeException() 方法。 使用catch語句,使execute拋出的SomeException異常不會(huì)中斷整個(gè)調(diào)用,而所謂中斷就意味著一種無法處理的崩潰。 如果調(diào)用者"懂得"如何處理一個(gè)異常、能夠正確地調(diào)用后續(xù)的流程,則應(yīng)該堅(jiān)決使用catch語句捕獲它以完成業(yè)務(wù)。 如果 execute() 可拋出多種異常,調(diào)用者對(duì)不同的異??梢杂胁煌臉I(yè)務(wù)流程時(shí),應(yīng)該分別catch,不能僅僅只是catch 住一個(gè)普通的基類異常。
異常的日志輸出
對(duì)于可預(yù)知的或者已經(jīng)處理的異常,可以根據(jù)業(yè)務(wù)需要不輸出日志,或者輸出簡單日志,例如:
try {
getUserInfo(userId);
} catch (TimeoutException e) {
//deal with e
logger.error("UserInfo timeout:" + userId);
}
而對(duì)于那些不可預(yù)知的,或者捕獲后未做任何處理的異常,則最好將異常信息輸出,以方便調(diào)試,例如:
try {
doSomething();
} catch (Exception e) {
//OR e.printStackTrace();
logger.error("", e);
}
注意:捕獲異常后不處理也不輸出log是一種非常不負(fù)責(zé)任的行為,這會(huì)造成問題很難被定位,極大地提高debug成本!
checked異常和unchecked異常的區(qū)別
checked異常
checked異常是指那些直接繼承自Exception的異常,如果一個(gè)方法聲明要拋出某種或者某幾種checked異常,那么此方法的調(diào)用者必須顯式地處理這些異常,或者也聲明要拋出這些異常,否則,代碼將無法編譯通過。例如:
//方法foo聲明可能會(huì)拋出IOException
public void foo() throws IOException {
if (somethingIsInvalid) {
throw new IOException();
}
}
//方法bar1調(diào)用foo,它需要處理foo拋出的IOException
public void bar1() {
try {
foo();
} catch (IOException e) {
//deal with e
}
}
//方法bar2調(diào)用foo,但是它無法處理foo拋出的IOException,則聲明自己也會(huì)拋出
//IOException,將異常丟給更上層的調(diào)用者來處理
public void bar2() throws IOException {
foo();
}
unchecked異常
unchecked異常是指那些直接繼承自RuntimeException的異常(比較特殊的是RuntimeException也是Exception的子類),它通常是由程序運(yùn)行時(shí)的錯(cuò)誤引起的,由于編譯器并不強(qiáng)制要求程序必須處理RuntimeException,所以叫unchecked異常。unchecked異常經(jīng)典例子是除0或者空指針:
public int divide(int n, int val) {
if (val == 0) {
throw new ArithmeticException("/ by zero");
}
return n / val;
}
public void foo(String s) {
if (s == null) {
throw new NullPointerException();
}
System.out.println(s);
}
微妙的區(qū)別
通過定義,很容易區(qū)分checked異常和unchecked異常,但是對(duì)于coder來說,在設(shè)計(jì)自定義異常的時(shí)候,何時(shí)使用checked異常,何時(shí)使用unchecked異常,的確是一個(gè)很微妙的話題。一般來講,checked異常是和具體應(yīng)用邏輯相關(guān)的異常,如文件沒有找到(FileNotFoundException),這些異常總可能發(fā)生,難以避免,并且底層邏輯希望由上層邏輯來處理這些異常情況,于是底層邏輯聲明要拋出一個(gè)checked異常。而unchecked異常通常用來表示那些程序運(yùn)行時(shí)發(fā)生的錯(cuò)誤,這些錯(cuò)誤通常有希望通過程序來避免,例如:NullPointerException可以通過程序判斷
if (ref != null) {
goAhead();
} else {
stop();
}
來避免。 也就是說unchecked異常存在的價(jià)值更傾向于幫助程序員發(fā)現(xiàn)程序中的錯(cuò)誤和不夠嚴(yán)謹(jǐn)?shù)倪壿嫞⑶倚迯?fù)這些問題。