Java異常處理之try...catch...語(yǔ)句的使用進(jìn)階
try就像一個(gè)網(wǎng),把try{}里面的代碼所拋出的異常都網(wǎng)住,然后把異常交給catch{}里面的代碼去處理。最后執(zhí)行finally之中的代碼。無(wú)論try中代碼有沒(méi)有異常,也無(wú)論catch是否將異常捕獲到,finally中的代碼都一定會(huì)被執(zhí)行。
雖然 Java 執(zhí)行時(shí)期系統(tǒng)所提供的預(yù)設(shè)處理器對(duì)除錯(cuò)很有用,你通常想要自己處理例外。這樣做有兩個(gè)優(yōu)點(diǎn):第一,它讓你修正錯(cuò)誤。第二,它可以避免程式自動(dòng)終止。每當(dāng)錯(cuò)誤發(fā)生時(shí),如果你的程式就停止而且列印出堆疊追蹤,大多數(shù)的使用者都會(huì)感到很困惑。很幸運(yùn),你很容易就能避免這種情形。
要防備并且處理執(zhí)行時(shí)期錯(cuò)誤,只要將你要監(jiān)視的程式碼放在 try 區(qū)塊里即可。在 try 區(qū)塊之后緊接著在 catch 子句里指定你希望捕捉的例外型態(tài)
錯(cuò)誤捕捉例子:
try { code; //將自己的代碼放在其中; } catch(e) //如果上面的代碼有錯(cuò)誤,這里就捕獲 { alert(e.number); //獲得錯(cuò)誤信息 }
例如:
import java.io.*;//調(diào)用io包 public class SimpleCharInOut { public static void main(String args[]) { char ch=' ';//定義個(gè)字符ch初始為‘ ' System.out.println(" Enter a character please");//在屏幕上輸出Enter a character please try {//你要監(jiān)視的程式碼放在 try 區(qū)塊里即可。在 try 區(qū)塊之后緊接著在 catch 子句里指定你希望捕捉的例外型態(tài) ch=(char)System.in.read();//將從鍵盤輸入的字符賦給ch } catch(IOException e) //如果上面的代碼有錯(cuò)誤,這里就捕獲 { } ;//錯(cuò)誤后不進(jìn)行操作 System.out.println("You're entered character:" + ch);// 在屏幕上輸出You're entered character: //和ch的值 } }
我們?cè)趯慗ava的try..catch的時(shí)候,往往需要在最后加上finally子句關(guān)閉一些IO資源,比如
InputStream is; try{ is=openInputStream(); // do something }catch(IOException e){ e.printStaceTrace(e); }finally{ try{ is.close(); }catch(IOException e ){ } }
但是在使用這種模式時(shí),即使是Java老手,偶爾也會(huì)犯一些錯(cuò)誤。比如上面這段代碼,當(dāng)openInputStream()函數(shù)在執(zhí)行過(guò)程中拋出異常,那么變量is的值仍為null,此時(shí)執(zhí)行is.close()會(huì)拋出NullPointerException. 由于NullPoiterException不是IOException的子類,因此它不能被catch塊捕獲,而是直接往調(diào)用層拋出去. 一種改進(jìn)的寫法就是在關(guān)閉流的時(shí)候先進(jìn)行非空判斷,但這樣代碼會(huì)顯得很啰嗦。個(gè)人認(rèn)為比較優(yōu)雅的寫法是直接調(diào)用commons-io包提供的IOUtils.closeQuitely()方法關(guān)閉流(或者自己封裝一個(gè)closeQuitely()方法)。
使用這種寫法還有一種好處,就是當(dāng)遇到關(guān)閉多個(gè)IO資源時(shí)不容易出錯(cuò),比如下面這段代碼:
InputStream is; OutputStream os ; try{ is=openInputStream(); // do something }catch(IOException e){ e.printStaceTrace(e); }finally{ try{ if (is != null ) is.close(); if (os != null ) os.close(); }catch(IOException e ){ } }
當(dāng)is.close()發(fā)生錯(cuò)誤的時(shí)候,os.close()就無(wú)法被執(zhí)行,從而導(dǎo)致os所引用的資源沒(méi)有被釋放。
也許Oracle也覺得這種try .. catch ... finally的樣板代碼太沒(méi)必要,因此在JDK 7中對(duì)try 子句進(jìn)行了一些改造,免去編寫一些手動(dòng)關(guān)閉資源的代碼,讓代碼看起來(lái)更緊湊更簡(jiǎn)潔。比如上面的代碼在JDK 7下可以改成:
try( InputStream is = openInputStream(); OutputStream os = openOutStream(); ){ // do something }catch(IOException e){ e.printStaceTrace(e); }
Oracle把這里的try(..)語(yǔ)句叫做try-with-resource語(yǔ)句。需要注意的是,try(.. )中變量所引用的對(duì)象都必須是實(shí)現(xiàn)了java.io.AutoClosable接口的實(shí)例,當(dāng)退出try ..catch塊時(shí),JDK會(huì)自動(dòng)調(diào)用close()方法。 也就是說(shuō),try-with-resource語(yǔ)句中的resource(資源)不僅限于IO資源。
這里有必要對(duì)try-with-resource語(yǔ)句的一些細(xì)節(jié)進(jìn)行補(bǔ)充說(shuō)明:
JDK會(huì)確保所有資源的close()方法被調(diào)用,不管close()方法是否拋出異常, 而調(diào)用的順序和資源聲明的順序相反
try-with-resource語(yǔ)句中所有拋出的異常都會(huì)被捕獲。如果多個(gè)異常被拋出,后面所拋出的異常會(huì)被suppress(抑制)在前一個(gè)異常中,catch塊最終只拿到最先拋出的那個(gè)異常。可以依次通過(guò)調(diào)用Throwable類定義的getSuppressed()獲得被suppressed(抑制)的異常。
還是上面那個(gè)例子,
當(dāng)退出try .. catch.塊的時(shí)候,JDK會(huì)先調(diào)用os.close(),然后是is.close(), 如果兩次close()都拋出IOException, 那么is.close()所拋出的異常會(huì)被suppress(抑制)在os.close()所拋出的異常中,最終catch塊只捕獲到os.close()所拋出的異常??梢酝ㄟ^(guò)getSuppressed()方法拿到is.close()所拋出的異常。
如果調(diào)用openInputStream()的時(shí)候就發(fā)生IOException,那么openOutputStream()就不會(huì)被調(diào)用,os.close()和is.close()也不會(huì)被調(diào)用, catch塊捕捉到 調(diào)用openInputStream()時(shí)所拋出的異常。
如果調(diào)用openOutputStream()發(fā)生IOException(用記號(hào) e1表示), 那么is.close()還是會(huì)被調(diào)用, 如果此時(shí)is.close()又拋出IOException(用記號(hào) e2表示),那么e2會(huì)被suppress到e1中,而catch塊捕捉到的異常是 e1.
除了對(duì)try塊做了改造,JDK 7還對(duì)catch部分進(jìn)行了簡(jiǎn)化,允許把多個(gè)catch子句合并。 比如:
try( InputStream is = openInputStream(); OutputStream os = openOutStream(); ){ // do something }catch(IOException | XMLParseException | XPathException e){ e.printStaceTrace(e); }
此外,當(dāng)你重新拋出多個(gè)異常時(shí),不再需要詳細(xì)定義異常類型了,編譯器已經(jīng)知道你具體拋出的是哪個(gè)異常了。你只需在方法定義的時(shí)候聲明需要拋出的異常即可。比如
// 雖然這里用Exception匹配拋出的IOException,到編譯器知道實(shí)際上拋給上層的異常是IOException public void doIO() throws IOException { try{ throw new IOException(); }catch(Exception e){ throw e; } }
PS : 這個(gè)特性我想不到會(huì)帶來(lái)什么好處
JDK 7還有其他有趣的語(yǔ)法新特性,比如二進(jìn)制字面量,用下劃線分割長(zhǎng)數(shù)字,泛型參數(shù)的類型推斷,switch支持字符串匹配等等。 現(xiàn)在JDK 8又引入了一些有用的特性。在不需要考慮向后兼容的前提下, 適當(dāng)并靈活運(yùn)用一些語(yǔ)法特性,可以讓我們的代碼在一定程度上顯得更清晰,更簡(jiǎn)潔。
相關(guān)文章
FutureTask為何單個(gè)任務(wù)僅執(zhí)行一次原理解析
這篇文章主要為大家介紹了FutureTask為何單個(gè)任務(wù)僅執(zhí)行一次原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-11-11SpringSecurity微服務(wù)實(shí)戰(zhàn)之公共模塊詳解
這篇文章主要為大家介紹了SpringSecurity微服務(wù)實(shí)戰(zhàn)之公共模塊詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08自己動(dòng)手用Springboot實(shí)現(xiàn)仿百度網(wǎng)盤的實(shí)踐
本項(xiàng)目基于Springboot開發(fā)實(shí)現(xiàn),前端采用BootStrap開發(fā)實(shí)現(xiàn),模仿百度網(wǎng)盤實(shí)現(xiàn)相關(guān)功能,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Eureka源碼閱讀解析Server服務(wù)端啟動(dòng)流程實(shí)例
這篇文章主要為大家介紹了Eureka源碼閱讀解析Server服務(wù)端啟動(dòng)流程實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-10-10java高級(jí)應(yīng)用:線程池的全面講解(干貨)
這篇文章主要介紹了java高級(jí)應(yīng)用:線程池的全面講解(干貨),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02Java實(shí)現(xiàn)并發(fā)執(zhí)行定時(shí)任務(wù)并手動(dòng)控制開始結(jié)束
這篇文章主要介紹了Java實(shí)現(xiàn)并發(fā)執(zhí)行定時(shí)任務(wù)并手動(dòng)控制開始結(jié)束,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Nacos下線服務(wù)時(shí),下線報(bào)錯(cuò)選舉Leader失敗問(wèn)題以及解決
這篇文章主要介紹了Nacos下線服務(wù)時(shí),下線報(bào)錯(cuò)選舉Leader失敗問(wèn)題以及解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-07-07