Java中的異常處理機制
0x00異常
異常分為Error和Exception,我們通常說的處理異常,其實是處理Exception。而Error已經(jīng)不是異常了,而是錯誤。一般是因為代碼錯誤導致jvm崩潰。
用圖說話

0x01 Exception類和它的子類
從上面的圖可以看到,Exception這個類下面有很多子類,他們都繼承自Exception,我們也可以自己寫一個異常類。例如:
public class MyException extends Exception {
}
然后就可以使用了,例如:
public static void throwException() throws MyException {
throw new MyException();
}
原理會在下文講到
這個繼承于Exceptiion的類啥都沒寫,所以類里面的東西和Exception里面的是一樣的。讓我們來看看Exception里面有些什么。好吧。。。全是super.method,我們還是看它的父類Throwable吧。
private transient Object backtrace;
private String detailMessage;
private Throwable cause = this;
主要就是這三個。第一個,是在拋出Exception后的棧跟蹤。第二個,是異常詳情。第三個就是異常原因,就是異常本身。
異常的構(gòu)造方法:
public Exception();
public Exception(String message);
public Exception(String message, Throwable cause);
public Exception(Throwable cause);
我就不具體寫了。跟成員變量對應。
jvm通過棧來將異常一層一層往上拋(與一層一層的函數(shù)調(diào)用相反),直到他被處理(catch),否則,程序停止工作,jvm向用戶報告錯誤。異常的拋出(棧)路徑可以通過Exception.printStackTrace()方法查看
而異常被處理后,程序會回到拋出異常的地方繼續(xù)執(zhí)行。
0x02 如何拋出異常和進行異常處理
-
拋出異常
很簡單,直接使用
throw關(guān)鍵字,但是如果拋出的異常未在當前方法處理,需要在方法后面聲明。需要在它所在的方法處聲明throws MyException。意思是此方法將拋出錯誤。注意:如果是
RuntimeException或者它的子類的話,可以不附加聲明,例如:try { // some code } catch (RemoteException e) { throw new RuntimeException(e); } -
處理異常
使用
try catch語句。用法:public static void main(String[] args) { try { throwException(); } catch (MyException e) { e.printStackTrace(); //Do sth... //throw e; } }try可能會拋出異常的語句塊,catch (MyException e)捕獲異常MyException聲明為引用e.一般來說都會跟上一個e.printStackTrace(),打印錯誤詳情,方便debug。
當然你也可以再次將異常拋出,交給上層繼續(xù)處理。
有人會發(fā)現(xiàn),e.printStackTrace()輸出的怎么是紅的,因為你看看它的源碼:public void printStackTrace() { printStackTrace(System.err); }它默認用的輸出流是
System.err,而不是sout用的System.out哦。 -
多重異常處理
用于將不同類型的異常分開處理。
try { throwException(); throw new NullPointerException(); } catch (MyException e) { e.printStackTrace(); } catch (NullPointerException e) { e.printStackTrace(); }
對了,如果程序因異常而退出,它的返回值就不為0,在IDEA中可以明顯看到:
Process finished with exit code xxx
0x03 finally關(guān)鍵字
你可能會遇到無論是否出現(xiàn)異常都需要進行某種操作的情況,這時候,你就需要用到finally了。比如:
try {
throwException();
throw new NullPointerException();
} catch (MyException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}finally {
System.out.println("An error occurred!");
}
這種方法經(jīng)常用來在異常發(fā)生后關(guān)閉流。
<del>其實可以把finally后面的語句看成擦屁股的</del>
0x04 異常丟失
這是一種特殊情況,雖然不經(jīng)常出現(xiàn),但是還是提一下比較好。
最簡單的觸發(fā)方法:
public static void main(String[] args) {
try {
throwException();
throw new NullPointerException();
} catch (MyException e) {
e.printStackTrace();
} catch (NullPointerException e) {
e.printStackTrace();
}finally {
System.out.println("An error occurred!");
return;
}
}
也就是在finally里面執(zhí)行return。
IDEA里面會提示:

其實原理就是在finally里面的語句會在異常處理完成之前執(zhí)行。如果在finnally里面return,就會發(fā)生異常丟失。
0x05 異常實例
private int take(int index, int last) {//取豆子
int count;
try {
count = mPrisoners.get(index).take(index, last);
} catch (Exception e) {//這些神經(jīng)病抓出來單獨死
count = -1;
}
if (count > last || count < 0){//如果返回無效個數(shù),和上面的神經(jīng)病一樣死
count = -1;
}
System.out.println(mPrisoners.get(index).getName() + "取了" + count + "個");
//保存每個人取的豆子數(shù)
mTempHold.replace(mPrisoners.get(index), count);
return count;
}
取豆子游戲,源碼在這兒,Manager類。
這是異常處理的用法之一,你永遠不知道熊孩子們會搞出什么異常炸掉你的程序,所以熊孩子必須抓出來單獨死。
異常在Java中使用頻率極高,多次使用對異常自然就熟悉了。