如果我們制作一個計(jì)算器程序,當(dāng)用戶輸入的除數(shù)為0時,程序?qū)罎⒅苯油顺瞿己荆敲丛摮绦虻挠脩趔w驗(yàn)將會非常差。我們應(yīng)該告訴用戶语盈,除數(shù)為0是不被允許的并繼續(xù)讓用戶使用該程序舱馅,而不是異常退出!這就要用到異常處理程序了刀荒。
簡單介紹
異常是指阻止當(dāng)前方法或作用域繼續(xù)執(zhí)行的問題代嗤。而異常處理的任務(wù)就是將程序從錯誤狀態(tài)中恢復(fù)棘钞,來讓程序要么換一種方式運(yùn)行,要么繼續(xù)運(yùn)行下去干毅!因此宜猜,我們在寫程序時需要考慮到程序?qū)霈F(xiàn)哪些異常,并將其捕獲處理硝逢,保證用戶不會接觸到它姨拥。
異常的繼承體系
如下圖,我們可以看到所有異常都是由Throwable
繼承而來趴捅。其后面還有兩個分支:Error
,Exception
垫毙。
當(dāng)出現(xiàn)Error問題時,一般都是系統(tǒng)內(nèi)部錯誤或者資源耗盡等才會發(fā)生拱绑,我們一般不關(guān)注此類問題(當(dāng)然也無能為力)。我們主要考慮程序本身將會出現(xiàn)的問題(Exception)丽蝎。我們一般主要將Exception
分為RuntimeException
與check Exception
猎拨。
出現(xiàn)RuntimeException時,表明程序本身存在錯誤屠阻,編譯時會通過红省,但運(yùn)行時會出現(xiàn)錯誤。此類異彻酰可以不捕捉處理或拋出吧恃,運(yùn)行時由JVM拋出給調(diào)用者或者虛擬機(jī)。
出現(xiàn)check Exception我們必須捕捉處理或者向上拋出給調(diào)用者麻诀,否則無法通過編譯處理痕寓。
語法說明
我們通常所說的異常捕獲,一般使用try
函數(shù)來完成蝇闭,異常處理一般使用 catch
函數(shù)完成呻率,處理完成一般還會使用finally
來處理其他一定要做的事情(比如數(shù)據(jù)庫的連接中斷、文件的讀取關(guān)閉)呻引。
try catch
捕獲異常
try{
//可能會出錯的程序
}
處理異常
//捕獲多個異常時礼仗,父類異常應(yīng)該在子類異常下面出現(xiàn)
try{
//可能會出錯的程序
} catch(Exception1 e){
//出現(xiàn)問題Exception1的處理方法
} catch(Exception2|Exception3 e){
//出現(xiàn)問題Exception2或者Exception3的處理方法
}
...
finally{
//處理完問題一定會執(zhí)行的代碼
}
拋出異常throws
//我們一直拋出異常直到傳遞給調(diào)用者
void func() throws Exception1,Exception2{
}
public static void main(String[] args) throws Exception1,Exception2{
func();
}
處理方式比較
異常處理過程
拋出異常時,首先逻悠,同Java中其他對象一樣元践,將使用new
在堆上創(chuàng)建異常對象。然后童谒,當(dāng)前路徑的執(zhí)行被終止单旁,并且從當(dāng)前環(huán)境中彈出對異常對象的引用。此時惠啄,異常處理機(jī)制開始接管程序并且使用異常處理程序
繼續(xù)執(zhí)行程序慎恒!異常處理程序的任務(wù)是將程序從錯誤中恢復(fù)并換一種方式運(yùn)行或者繼續(xù)執(zhí)行下去任内!
異常處理的兩種模型
終止模型
:一旦異常被拋出,就表明異常已經(jīng)無法挽回融柬,也不能回來繼續(xù)執(zhí)行死嗦!
恢復(fù)模型
:希望能夠處理異常并繼續(xù)執(zhí)行程序,此時粒氧,異常不能拋出只能調(diào)用方法修正該錯誤越除,或者,把try塊放到while循環(huán)里外盯,不斷進(jìn)入try摘盆,直到得到滿意結(jié)果為止!
對于兩種模型饱苟,程序員大多最終轉(zhuǎn)向了終止模型的代碼孩擂,并且忽略恢復(fù)行為。因?yàn)榛謴?fù)模型可能導(dǎo)致的耦合性:恢復(fù)性的處理程序需要了解異常拋出的地點(diǎn)箱熬,這勢必要包含依賴于拋出位置的非通用性代碼类垦,這增加了代碼的編寫與維護(hù)的困難。因此我們應(yīng)該盡量解決能解決的問題城须,其他的都拋給調(diào)用者蚤认!
自定義異常
簡單介紹
我們已經(jīng)知道異常都是基于Throwable
的,但我們一般只關(guān)注Exception
糕伐。因此我們自定義的異常類一般都是Exception的子類砰琢。
- 異常有兩個構(gòu)造器,有參構(gòu)造器的參數(shù)是出現(xiàn)異常時你想要顯示的信息良瞧。
- 方法內(nèi)部我們使用
throw
拋出異常陪汽,方法要想向上拋出異常需要使用throws
- try塊中,如果異常已經(jīng)觸發(fā)莺褒,try中異常觸發(fā)后下面的代碼不執(zhí)行
- 我們可以使用
e.getMessage()
掩缓,e.toString()
,e.printStackTrace();
顯示錯誤信息,前兩者需要借助print輸出遵岩,且三者輸出的信息量呈遞增你辣。 -
e.getMessage()
只輸出構(gòu)造器收到的異常信息,e.toString()
輸出異常名稱與構(gòu)造器收到的異常信息尘执,e.printStackTrace();
除兩者外還顯示異常代碼的位置舍哄。 -
e.printStackTrace();
方法默認(rèn)會通過System.err
將錯誤發(fā)送給標(biāo)準(zhǔn)錯誤流,e.printStackTrace(System.out);
會將信息發(fā)送給輸出流誊锭。 - 在catch塊中我們可以重新拋出異常表悬,但調(diào)用者需要使用throws來聲明。
代碼演示
class SimpleException extends Exception {
public SimpleException() {}
public SimpleException(String msg) {
super(msg);
}
}
public class ExceptionDemo {
public void func() throws SimpleException {
System.out.println("從func()拋出異常丧靡!");
throw new SimpleException();
}
public void func1() throws SimpleException {
System.out.println("從func1()拋出異常蟆沫!");
throw new SimpleException("func1異常");
}
public static void main(String[] args) throws SimpleException {
ExceptionDemo me = new ExceptionDemo();
try {
me.func();
} catch (SimpleException e) {
System.out.println("拋出異常籽暇!");
e.printStackTrace();
}
try {
me.func1();
System.out.println("錯誤已引發(fā),此處不執(zhí)行");
} catch (SimpleException e) {
System.out.println(e.getMessage());
System.out.println(e.toString());
e.printStackTrace(System.out);
throw e;//重新拋出異常
}
}
}
catch中拋出異常
-
記錄異常后重新拋出異常:
try{ //可能存在異常的代碼 }catch(Exception e){ logger.log(level,message,e); throw e; }
-
拋出新異常
void func() throws ServletException { try { //可能存在異常的代碼 } catch (Exception e) {//細(xì)化異常饭庞,重新拋出新異常 Exception se = ServletException("database error"); se.initCause(e); throw se; //Exception e =se.getCause(); //還原舊異常 } }
異常使用技巧
- 異常處理不能代替簡單的測試戒悠,比如說退棧操作不要異常處理
- 不要過分的細(xì)化異常:盡量使用少的try舟山,catch完成工作
- 利用異常層次結(jié)構(gòu)绸狐,拋出合理異忱鄣粒或自定義異常寒矿;不要只拋出RuntimeException或者Throwable
- 不要壓制異常
關(guān)于斷言與日志記錄若债,后續(xù)學(xué)習(xí)符相!
本人小白一個蠢琳,歡迎訪問我的個人博客主巍,同時也歡迎來相互交流學(xué)習(xí)挪凑!