1屋彪、什么是異常
結(jié)構(gòu)不佳的代碼不能運(yùn)行,這是Java的基本理念绒尊。
發(fā)現(xiàn)錯(cuò)誤的理想時(shí)機(jī)是在編譯期畜挥。然而,編譯器并不能發(fā)現(xiàn)所有的錯(cuò)誤婴谱,余下的問題就需要在程序運(yùn)行時(shí)解決蟹但。這就需要錯(cuò)誤能通過某種方式,把適當(dāng)?shù)男畔?傳遞給特定的接收者處理谭羔。Java中的異常處理的目的在于通過使用少量的代碼來簡化大型华糖、可靠的程序的生成,通過此方式讓你的應(yīng)用中沒有未處理的錯(cuò)誤瘟裸,而 且它還帶來了一個(gè)明顯的好處:降低錯(cuò)誤處理代碼的復(fù)雜度客叉。
異常,根據(jù)字面理解话告,有意外之意兼搏。把它置于代碼層面來理解,即阻止了當(dāng)前方法或作用域繼續(xù)執(zhí)行沙郭。
在Java中佛呻,異常被當(dāng)做對象來處理,其基類是Throwable病线。
2吓著、Java中的異常類型
Java從Throwable直接派生出Exception和Error鲤嫡。其中Exception是可以拋出的基本類型,在Java類庫绑莺、方法以及運(yùn)行時(shí)故障中都可能拋出Exception型異常暖眼。Exception表示可以恢復(fù)的異常,是編譯器可以捕捉到的紊撕;Error表示編譯時(shí)和系統(tǒng)錯(cuò)誤罢荡,表示系統(tǒng)在運(yùn)行期間出現(xiàn)了嚴(yán)重的錯(cuò)誤,屬于不可恢復(fù)的錯(cuò)誤对扶,由于這屬于JVM層次的嚴(yán)重錯(cuò)誤区赵,因此這種錯(cuò)誤會(huì)導(dǎo)致程序終止執(zhí)行。Exception又分為檢查異常和運(yùn)行時(shí)異常浪南。
異常類的結(jié)構(gòu)層次圖如下:
典型的RuntimeException(運(yùn)行時(shí)異常)包括NullPointerException, ClassCastException(類型轉(zhuǎn)換異常)笼才,IndexOutOfBoundsException(越界異常), IllegalArgumentException(非法參數(shù)異常),ArrayStoreException(數(shù)組存儲異常),AruthmeticException(算術(shù)異常),BufferOverflowException(緩沖區(qū)溢出異常)等;
非RuntimeException(檢查異常)包括IOException, SQLException,InterruptedException(中斷異常-調(diào)用線程睡眠時(shí)候),NumberFormatException(數(shù)字格式化異常)等络凿。
而按照編譯器檢查方式劃分骡送,異常又可以分為檢查型異常(CheckedException)和非檢查型異常 (UncheckedException)。Error和RuntimeException合起來稱為UncheckedException絮记,之所以這么 稱呼摔踱,是因?yàn)榫幾g器不檢查方法是否處理或者拋出這兩種類型的異常,因此編譯期間出現(xiàn)這種類型的異常也不會(huì)報(bào)錯(cuò)怨愤,默認(rèn)由虛擬機(jī)提供處理方式派敷。除了Error 和RuntimeException這兩種類型的異常外,其它的異常都稱為Checked異常撰洗。
3篮愉、Java如何處理異常
3.1 try-catch, try-finally, try-catch-finally
對于checked類型異常,我們要么對它進(jìn)行處理差导,要么在方法頭使用throws拋出试躏。
public static void createFile() throws IOException{
File file = new File("C:/test.txt");
if(!file.exists()){
file.createNewFile();
}
}
public static void main(String[] args) {
try {
createFile();
} catch (IOException ex) {
// handle exception here
}
}
關(guān)于catch需要注意的幾點(diǎn):
1)、參數(shù)的異常類型必須是Throwable類或者其子類设褐。
2)颠蕴、從上往下的catch語句,其參數(shù)類型必須按照從子類到父類順序助析,因?yàn)橐坏┢ヅ涞揭粋€(gè)類型裁替,就會(huì)忽略往后的catch。比如IOException必須放到Exception前面貌笨,否則編譯器會(huì)報(bào)錯(cuò)弱判。
3)、可以有一個(gè)或者多個(gè)catch語句锥惋,甚至如果有finally語句的情況下昌腰,可以沒有catch語句开伏,如try-finally。
想要捕獲多個(gè)異常遭商,可以使用多個(gè)catch語句固灵,JDK7以后提供了另外一種方式:多重捕獲(multi-catch)。
try{
// other code
} catch (IOException | SQLException ex) {
throw ex;
}
4)劫流、不要忽略異常巫玻。空的catch塊會(huì)使異常達(dá)不到應(yīng)有的目的祠汇,除非諸如關(guān)閉FileInputStream的時(shí)候仍秤,因?yàn)槟氵€沒有改變文件的狀態(tài),因此不必執(zhí)行任何恢復(fù)動(dòng)作可很,并且已經(jīng)從文件中讀取到所需要的信息诗力,因此不用終止正在進(jìn)行的操作。
關(guān)于finally需要注意的幾點(diǎn):
1)我抠、finally中的代碼總是會(huì)被執(zhí)行苇本,除非在執(zhí)行try或者catch語句時(shí)虛擬機(jī)退出(System.exit(1))。
2)菜拓、finally塊可以做一些資源清理工作瓣窄,如關(guān)閉文件、關(guān)閉游標(biāo)等操作纳鼎。
3)康栈、finally塊不是必須的。
另外喷橙,如果在try和finally塊中都執(zhí)行了return語句,最終返回的將是finally中的return值登舞。
3.2 異常鏈
常常想要在捕獲一個(gè)異常后拋出另外一個(gè)異常贰逾,并且希望把原始異常信息保存下來,這就是異常鏈菠秒。在JDK1.4以后疙剑,Throwable子類在構(gòu)造器 中可以接受一個(gè)cause對象作為參數(shù),表示原始異常践叠,通過這樣把原始異常傳遞給新的異常言缤,使得即使在當(dāng)前位置創(chuàng)建并拋出了新的異常,也能通過這個(gè)異常鏈 追蹤到異常最初發(fā)生的位置禁灼。
但在Throwable子類中管挟,只有Error, Exception, RuntimeException三類異常類提供了帶cause參數(shù)的構(gòu)造器,其它類型的異常則需要通過initCause()方法弄捕。例如定義了CustomException類僻孝,可以這樣使用:
CustomException cmex = new CustomException();
cmex.initCause(new NullPointerException);
throw cmex;
這樣一來导帝,CustomException繼承自Exception或RuntimeException,就屬于自定義異常了穿铆。
一般來說您单,自定義異常的作用有以下情形:
1)、將檢查型異常轉(zhuǎn)換為非檢查型異常荞雏。
2)虐秦、在產(chǎn)生異常時(shí)封裝上下文信息、定義異常碼凤优、收集環(huán)境對象悦陋,有利于信息的傳遞。
4别洪、異常使用指南
1)叨恨、在知道該如何處理的情況下才捕獲異常。
2)挖垛、自定義異常類型痒钝,用以封裝所有的檢查型異常。
3)痢毒、在程序的邊界進(jìn)行異常捕獲送矩。如服務(wù)端相應(yīng)客戶端的請求,在出口處catch內(nèi)部有可能產(chǎn)生的異常哪替,并統(tǒng)一throw一個(gè)封裝過的異常給客戶端栋荸,免得暴露服務(wù)端敏感信息。
4)凭舶、只針對異常的情況才使用異常晌块。不要在所有的代碼中習(xí)慣性地使用try-catch,因?yàn)檫@會(huì)影響性能帅霜。
5)匆背、拋出與抽象相對的異常。如果方法拋出的異常與它執(zhí)行的任務(wù)沒有明顯的聯(lián)系身冀,這種情形會(huì)使人不知所措钝尸。為了避免這個(gè)問題,更高層的實(shí)現(xiàn)應(yīng)該捕獲 低層的異常搂根,同時(shí)拋出可以按照高層抽象進(jìn)行解釋的異常珍促,這種做法被稱為異常轉(zhuǎn)譯(exception translation),如下:
try{
// use lower-level abstraction to do our bidding
} catch(LowerLevelException ex){
throw new HigherLevelException(...);
}
另外一種特殊的異常轉(zhuǎn)譯稱為異常鏈剩愧,上面已作描述猪叙。如果低層的異常對于調(diào)試導(dǎo)致高層異常的問題非常有幫助,使用異常鏈就很合適。高層的異常提供訪問方法(Throwable.getCause)來獲得低層的異常沐悦。
6)成洗、每個(gè)方法拋出的異常要有文檔描述。利用Javadoc的@throws標(biāo)記藏否,記錄拋出每個(gè)異常的條件瓶殃。如果一個(gè)方法可能拋出多個(gè)異常,不要使 用這些異常類的某個(gè)超類副签。如不要聲明一個(gè)方法“throws Exception”或“throws Throwable”遥椿,這將沒有任何指導(dǎo)信息。