Java中的異常(上)

沒有一名程序員希望自己在寫程序的時(shí)候遇到異常,但是實(shí)際上異常是無法避免的,沒有人能保證寫出的程序不會(huì)出錯(cuò),已無法保證用戶會(huì)按照程序員的意愿來使用程序枝冀。既然異常無法避免,對異常的處理也就是必需的耘子。異常處理已成為衡量一門語言是否成熟的標(biāo)準(zhǔn)之一果漾,增加了異常處理機(jī)制后的程序有更好的容錯(cuò)性、更加健壯谷誓。目前的主流編程語言(如C++绒障、C#、Python等)都提供了這種機(jī)制捍歪,Java也不例外户辱。Java的異常機(jī)制主要依賴于trycatch糙臼、finally庐镐、throwsthrow這五個(gè)關(guān)鍵字。

最基本的異常處理——try...catch

下面是Java異常處理機(jī)制的語法結(jié)構(gòu):

try{
    //業(yè)務(wù)邏輯代碼
    ...
}catch(Exception e){
    //異常處理代碼
    ...
}

如果try塊代碼出現(xiàn)異常变逃,系統(tǒng)會(huì)自動(dòng)生成一個(gè)異常對象必逆,該異常對象被提交給Java運(yùn)行時(shí)環(huán)境(Runtime Environment),這個(gè)過程稱為拋出(throw)異常。
當(dāng)Java運(yùn)行時(shí)環(huán)境接收到異常對象時(shí)名眉,會(huì)自動(dòng)尋找處理該異常對象的catch塊粟矿。如果找到了合適的catch塊,則把該異常對象交給改catch塊處理璧针,這個(gè)過程稱為捕獲(catch)異常;如果Java運(yùn)行時(shí)環(huán)境無法找到捕獲異常的catch塊渊啰,則運(yùn)行時(shí)環(huán)境終止探橱,Java程序也隨之退出。這就是Java中最基本的異常處理機(jī)制绘证,即先拋出異常隧膏,再捕獲異常(并處理)。
注意:try塊與if語句不一樣嚷那,即使try塊里只有一條語句胞枕,花括號(hào){}也不能省略。

異常類的繼承體系

catch塊都是專門用于處理異常類及其子類的異常實(shí)例魏宽。這句戶乍一看很難理解腐泻,來看下面的例子:

異常的繼承體系1

結(jié)合上圖進(jìn)行解釋:當(dāng)Java運(yùn)行時(shí)環(huán)境接收到異常對象后,會(huì)一次判斷該異常對象是否是catch塊后異常類或其子類的實(shí)例队询,如果是派桩,Java運(yùn)行時(shí)環(huán)境將調(diào)用該catch塊來處理該異常;否則將再次拿該異常對象和下一個(gè)catch塊后的異常類進(jìn)行比較蚌斩,直至滿足條件铆惑。
因此,try塊后可以有多個(gè)catch塊送膳,這是為了針對不同的異常類提供不同的異常處理方式员魏。當(dāng)系統(tǒng)發(fā)生不同的意外情況時(shí),系統(tǒng)會(huì)生成不同的異常對象叠聋,Java運(yùn)行時(shí)環(huán)境就會(huì)根據(jù)該異常對象所屬的異常類來決定使用哪個(gè)catch塊來處理該異常撕阎。同時(shí),通過在try塊后提供多個(gè)catch塊碌补,也無須在一個(gè)catch塊內(nèi)使用ifswitch等語句判斷異常類型進(jìn)而采取不同的處理方式闻书,使得異常處理邏輯更加細(xì)致、更有條理脑慧。
再來看一張圖:

異常類的繼承體系2

Java把所有的非正常情況分成兩種:異常(Exception)和錯(cuò)誤(Error)魄眉。Error錯(cuò)誤,一般是指與虛擬機(jī)(JVM)相關(guān)的問題闷袒,如系統(tǒng)崩潰坑律、虛擬機(jī)錯(cuò)誤、動(dòng)態(tài)連接失敗等,這種錯(cuò)誤無法恢復(fù)或不可能捕獲晃择,將導(dǎo)致應(yīng)用程序中斷冀值。通常應(yīng)用程序無法處理這些錯(cuò)誤,因此應(yīng)用程序不應(yīng)該試圖使用catch塊來捕獲Error對象宫屠。而對于異常列疗,看個(gè)例子:

public class NullTest {
    public static void main(String[] args) {
        Date d = null;
        try {
            System.out.println(d.after(new Date()));
        } catch (NullPointerException e) {
            System.out.println("空指針異常!");
        } catch (Exception e) {
            System.out.println("未知異常浪蹂!");
        }
    }
}

上面程序調(diào)用了一個(gè)null對象的after()方法抵栈,因此引發(fā)了NullPointerException。注意到程序中把對應(yīng)Exception類的catch塊放在最后坤次,根據(jù)異常類的繼承體系可知古劲,這是因?yàn)槿绻褜?yīng)Exception類的catch塊放在其他catch塊的前面,出現(xiàn)異常后程序會(huì)直接進(jìn)入該catch塊缰猴,而排在它后面的catch塊將永遠(yuǎn)不會(huì)被執(zhí)行产艾,這就違背了上文提到的對不同異常進(jìn)行不同處理的原則。
實(shí)際上滑绒,進(jìn)行異常捕獲時(shí)不僅應(yīng)該把Exception類對應(yīng)的catch塊放在最后闷堡,而且所有父類異常的catch塊都應(yīng)該排在子類異常catch塊的后面(簡稱:先處理小異常,再處理大異常)疑故,否則會(huì)出現(xiàn)編譯錯(cuò)誤缚窿。將上面的代碼稍作修改:

...
try {
    System.out.println(d.after(new Date()));
} catch (Exception e) {
    System.out.println("未知異常!");
} catch (NullPointerException e) {
    System.out.println("空指針異常焰扳!");
}

這時(shí)程序報(bào)錯(cuò):

程序報(bào)錯(cuò)

錯(cuò)誤信息是:這個(gè)異常已被Exception類的catch塊捕獲了倦零。

訪問異常信息

所有的異常對象都包含下面四個(gè)方法:

  • getMessage():返回該異常的詳細(xì)描述字符串;
  • printStackTrace():將該異常的跟蹤棧信息輸出到標(biāo)準(zhǔn)錯(cuò)誤輸出吨悍;
  • printStackTrace(PrintStream s):將該異常的跟蹤棧信息輸出到指定輸出流扫茅;
  • getStackTrace():返回該異常的跟蹤棧信息。

還是剛才的例子:

...
try {
    System.out.println(d.after(new Date()));
} catch (NullPointerException e) {
    e.printStackTrace();
}

控制臺(tái)輸出為:

異常的跟蹤棧信息

使用finally回收資源

有些時(shí)候育瓜,程序在try塊里打開了一些物理資源(例如數(shù)據(jù)庫連接葫隙、網(wǎng)絡(luò)連接和磁盤文件等),這些物理資源都必須顯示回收躏仇,因?yàn)镴ava的垃圾回收機(jī)制只回收堆內(nèi)存中對象所占用的內(nèi)存恋脚,不會(huì)回收任何物理資源。為解決這個(gè)問題焰手,Java的異常處理機(jī)制提供了finally塊糟描。不管try塊中的代碼是否出現(xiàn)異常,也不管哪一個(gè)catch塊被執(zhí)行书妻,甚者在try塊或者catch塊中執(zhí)行了return語句船响,finally塊總會(huì)被執(zhí)行。因此,完整的Java異常處理語法結(jié)構(gòu)為:

try{
    //業(yè)務(wù)邏輯代碼
    ...
}catch(Exception e){
    //異常處理代碼
    ...
}...
finally{
    //資源回收代碼
}

注意:異常語法結(jié)構(gòu)中只有try塊是必需的见间;catch塊和finally塊都是可選的聊闯,但至少出現(xiàn)其中之一,也可以同時(shí)出現(xiàn)米诉;finally塊必須位于所有的catch塊之后菱蔬。
來看一個(gè)例子:

try {
    System.out.println(d.after(new Date()));
} catch (NullPointerException e) {
    e.printStackTrace();
    return;
} finally {
    System.out.println("finally塊里的語句被執(zhí)行了!");
}

控制臺(tái)輸出為:

控制臺(tái)輸出

程序的catch塊有一條return語句史侣。在通常情況下拴泌,一旦在方法里執(zhí)行到return語句的地方,程序?qū)⒘⒓唇Y(jié)束該方法抵窒。但由控制臺(tái)輸出的結(jié)果可知弛针,雖然return語句也強(qiáng)制方法結(jié)束叠骑,但一定會(huì)執(zhí)行finally塊里的語句李皇。
作為對比,對上面的代碼稍作修改:

try {
    System.out.println(d.after(new Date()));
} catch (NullPointerException e) {
    e.printStackTrace();
    System.exit(1);
} finally {
    System.out.println("finally塊里的語句被執(zhí)行了宙枷!");
}

再看控制臺(tái)的輸出:

控制臺(tái)輸出

程序中使用System.exit(1)語句來退出虛擬機(jī)掉房,此時(shí)finally塊將失去被執(zhí)行的機(jī)會(huì)。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末慰丛,一起剝皮案震驚了整個(gè)濱河市卓囚,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌诅病,老刑警劉巖哪亿,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贤笆,居然都是意外死亡蝇棉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門芥永,熙熙樓的掌柜王于貴愁眉苦臉地迎上來篡殷,“玉大人,你說我怎么就攤上這事埋涧“辶桑” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵棘催,是天一觀的道長劲弦。 經(jīng)常有香客問我,道長醇坝,這世上最難降的妖魔是什么瓶您? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上呀袱,老公的妹妹穿的比我還像新娘贸毕。我一直安慰自己,他們只是感情好夜赵,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布明棍。 她就那樣靜靜地躺著,像睡著了一般寇僧。 火紅的嫁衣襯著肌膚如雪摊腋。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天嘁傀,我揣著相機(jī)與錄音兴蒸,去河邊找鬼。 笑死细办,一個(gè)胖子當(dāng)著我的面吹牛橙凳,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播笑撞,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼岛啸,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茴肥?” 一聲冷哼從身側(cè)響起坚踩,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瓤狐,沒想到半個(gè)月后瞬铸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡础锐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年嗓节,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片郁稍。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡赦政,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出耀怜,到底是詐尸還是另有隱情恢着,我是刑警寧澤,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布财破,位于F島的核電站掰派,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏左痢。R本人自食惡果不足惜靡羡,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一系洛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧略步,春花似錦描扯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至杭煎,卻和暖如春恩够,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背羡铲。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工蜂桶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人也切。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓扑媚,卻偏偏與公主長得像,于是被迫代替她去往敵國和親贾费。 傳聞我的和親對象是個(gè)殘疾皇子钦购,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法檐盟,類相關(guān)的語法褂萧,內(nèi)部類的語法,繼承相關(guān)的語法葵萎,異常的語法导犹,線程的語...
    子非魚_t_閱讀 31,625評論 18 399
  • 通俗編程——白話JAVA異常機(jī)制 - 代碼之道,編程之法 - 博客頻道 - CSDN.NEThttp://blog...
    葡萄喃喃囈語閱讀 3,179評論 0 25
  • 初識(shí)異常(Exception) 比如我們在取數(shù)組里面的某個(gè)值得時(shí)候羡忘,經(jīng)常會(huì)出現(xiàn)定義的取值范圍超過了數(shù)組的大小谎痢,那么...
    iDaniel閱讀 1,869評論 1 2
  • iOS10 從2017年1月1日起蘋果不允許我們通過這個(gè)方法跳過ATS,也就是說強(qiáng)制我們用HTTPS卷雕,如果不這樣的...
    moodi閱讀 360評論 0 1
  • 一、前言 1浸间、寫了10多天的小程序代碼太雨,有興趣的可以看我這篇小程序官方文檔-小程序版【持續(xù)更新】,被坑得有點(diǎn)暈魁蒜,突...
    gitKong閱讀 2,091評論 14 30