摘要:所有能夠處理該異常的方法,都來(lái)自一個(gè)叫做調(diào)用堆棧的方法列表。如果運(yùn)行環(huán)境在調(diào)用堆棧中自始至終未能找到捕獲這個(gè)異常的代碼塊,那么整個(gè)程序?qū)⒔K止運(yùn)行。
本文嘗試以盡可能詳細(xì)的方式介紹 Java 當(dāng)中的異常概念和處理機(jī)制。本文適合 Java 初學(xué)者閱讀。
什么是異常異常是發(fā)生在程序運(yùn)行過(guò)程中的,阻斷正常流程中的指令執(zhí)行的事件。
當(dāng)一個(gè)方法在執(zhí)行當(dāng)中發(fā)生錯(cuò)誤時(shí),這個(gè)方法就會(huì)創(chuàng)建一個(gè)特別的對(duì)象,將其交付給 Java 運(yùn)行環(huán)境處理。這個(gè)對(duì)象被稱(chēng)作“異常對(duì)象”(或簡(jiǎn)稱(chēng)異常),它當(dāng)中包含了異常事件的類(lèi)型和狀態(tài)等等信息。 創(chuàng)建這個(gè)對(duì)象并將其交付給 Java 運(yùn)行環(huán)境的過(guò)程,稱(chēng)作“拋出異?!?。
異常對(duì)象:
所有的異常對(duì)象都繼承于 java.lang.Exception 類(lèi)。不同類(lèi)型的異常被定義成它的不同子類(lèi)。常見(jiàn)的異常類(lèi)型有 java.io.IOException(讀寫(xiě)異常),java.lang.NullPointerException(空指針異常)等。
當(dāng)一個(gè)方法拋出異常(或“異常對(duì)象”)時(shí),運(yùn)行環(huán)境就會(huì)嘗試尋找某個(gè)方法去處理它。所有能夠處理該異常的方法,都來(lái)自一個(gè)叫做“調(diào)用堆棧”的方法列表。這個(gè)列表的最頂層就是拋出該異常的方法,往下是調(diào)用該方法的方法,然后依次類(lèi)推。
例如a()方法當(dāng)中調(diào)用了b()方法,b()方法當(dāng)中調(diào)用了c()方法,那么當(dāng)c()方法拋出異常時(shí),它就在調(diào)用堆棧的最頂層,往下是b()方法,再往下就是a()方法。
運(yùn)行環(huán)境會(huì)沿著調(diào)用堆棧往下尋找,尋找方法當(dāng)中是否存在能夠處理這個(gè)異常的代碼塊。這樣的代碼塊叫做“捕獲異?!?。運(yùn)行環(huán)境將異常對(duì)象交給這個(gè)“捕獲異?!钡拇a塊處理。如果運(yùn)行環(huán)境在調(diào)用堆棧中自始至終未能找到捕獲這個(gè)異常的代碼塊,那么整個(gè)程序?qū)⒔K止運(yùn)行。
處理異常的方式:捕獲或聲明當(dāng)你編寫(xiě)一個(gè)方法 a(),當(dāng)中調(diào)用了方法 b(),而該方法可能拋出異常,那么你有兩種選擇:
一、在 a() 方法中用 try-catch 結(jié)構(gòu)捕獲 b() 方法拋出的異常,方式如下:
// 示例1 try { ... b(); ... } catch (Exception e) { // 處理異常對(duì)象 }
二、在 a() 方法上聲明為拋出 b() 方法所拋出的異常,像這樣:
// 示例2 public void a() throws Exception { ... b(); ... }
這樣的話,調(diào)用 a() 方法的那個(gè)方法,同樣面臨兩種選擇:捕獲,或繼續(xù)拋出。如果你不在這兩種處理方式當(dāng)中選擇一種,那么編譯器就會(huì)判定你的代碼存在錯(cuò)誤,并拒絕編譯。
開(kāi)始的時(shí)候說(shuō)過(guò),異常事件會(huì)阻斷指令的執(zhí)行。也就是說(shuō),執(zhí)行到 b() 方法的時(shí)候,如果它拋出了異常,那么它后面的指令都不會(huì)執(zhí)行了。比如在第一種情況下(示例 1)拋出異常,那么從 b(); 到 } catch (Exception e) { 之間的所有語(yǔ)句都不會(huì)執(zhí)行;在第二種情況下(示例 2)則是從 b(); 到 a() 方法結(jié)束之間的所有語(yǔ)句都不會(huì)執(zhí)行。
接下來(lái)我們分別介紹這兩種處理方式。
捕獲異常的方式:try-catch-finally在 Java 中,捕獲異常的方式是編寫(xiě) try-catch-finally 代碼塊。下面是一個(gè)例子,讀取指定文件并將文件內(nèi)容輸出到控制臺(tái):
// 示例3 public static void main(String[] args) { java.io.File file = new File("C:1.txt"); java.io.BufferedReader reader = null; String line; try { reader = new BufferedReader( new java.io.FileReader(file)); // 1 while ((line = reader.readLine()) != null) { // 2 System.out.println(line); } } catch (IOException e) { // 3 e.printStackTrace(); } finally { // 4 if (reader != null) { try { reader.close(); // 5 } catch (Exception e) { e.printStackTrace(); } } } }
上面的例子中,1 處的 new FileReader(file) 和 [2] 處的 readLine() 方法都可能拋出 java.io.IOException,所以我們用 try {...} 將它們包起來(lái),表示這部分代碼執(zhí)行的時(shí)候可能拋出異常。
[3] 處為 catch 塊,這部分決定了當(dāng)異常真的發(fā)生時(shí),該如何處理。假如 1 處拋出了異常,那么 Java 運(yùn)行環(huán)境將跳過(guò) try 塊后面的 while 部分,直接轉(zhuǎn)到 catch 塊來(lái)處理這個(gè)異常。
catch 后面的 (IOException e) 聲明了被捕獲的異常對(duì)象,你可以在 catch 塊中使用它。在這里我們簡(jiǎn)單的調(diào)用 e.printStackTrace() 方法,將異常信息輸出到控制臺(tái)。
注意,try 塊中拋出的異常類(lèi)型必須與 catch 塊聲明所要捕獲的異常類(lèi)型匹配,否則編譯器將拒絕編譯。關(guān)于這一點(diǎn)的詳情將在后面說(shuō)明。
[4] 處為 finally 塊,這部分決定了當(dāng) try 塊和/或 catch 塊執(zhí)行完畢后,還要執(zhí)行哪些代碼,這部分可以看作是“總是會(huì)執(zhí)行的”、“善后工作”。在這里我們嘗試關(guān)閉 reader 對(duì)象,不論是否出現(xiàn)異常。
注意 reader.close(); 方法也可能拋出異常,所以 [5] 這里又需要用 try-catch-finally 塊包起來(lái)。你當(dāng)然已經(jīng)注意到,這里沒(méi)有 finally 部分。其實(shí) try-catch-finally 塊有三種寫(xiě)法,分別是:
1. try {...A...} catch(Exception e) {...B...} finally {...C...} 2. try {...A...} catch(Exception e) {...B...} 3. try {...A...} finally {...C...}
寫(xiě)法 1 的意思是:嘗試 A;如果出現(xiàn)異常則執(zhí)行 B;不管是否出現(xiàn)異常,都執(zhí)行 C。
寫(xiě)法 2 的意思是:嘗試 A;如果出現(xiàn)異常則執(zhí)行 B。
寫(xiě)法 3 的意思是:嘗試 A;如果出現(xiàn)異常則不理會(huì)拋出去;不管是否出現(xiàn)異常,都執(zhí)行 C。
在寫(xiě)法 3 中,如果 try 部分拋出了異常,則需要在當(dāng)前方法中聲明。聲明拋出異常的方式,將在接下來(lái)說(shuō)明。
聲明拋出異常任何一個(gè)方法都可以通過(guò) throws 關(guān)鍵字聲明自己可能拋出異常。下面是一個(gè)例子:
// 示例4 public void f() throws Exception { ... }
在這個(gè)例子中,方法 f() 聲明自己可能會(huì)拋出 java.lang.Exception 類(lèi)型的異常。除了 Exception 外,方法也可以明確的指出自己可能拋出哪種類(lèi)型的異常,例如:
// 示例5 public void f() throws java.io.IOException { ... }
如果方法當(dāng)中的某條語(yǔ)句可能會(huì)拋出 A 類(lèi)型的異常,那么方法就不能聲明為拋出 B 類(lèi)型的異常。下面的做法是錯(cuò)誤的:
// 示例6 public void g() throws java.lang.NullPointerException { f(); }
因?yàn)?f() 已經(jīng)聲明為拋出 IOException,而 g() 并沒(méi)有將該異常捕獲,因此它也必須聲明為 “throws IOException”。如果編譯器發(fā)現(xiàn)這種不匹配的情況,就會(huì)拒絕編譯。
有一種情況例外,就是 B 類(lèi)型的異常是 A 類(lèi)型的異常的父類(lèi)。這意味著 B 類(lèi)型的異常在業(yè)務(wù)邏輯上涵蓋了 A 類(lèi)型,所以我們相信凡是能處理 B 的自然也能處理 A。
因此,既然 java.lang.Exception 是所有異常的父類(lèi),那么凡是聲明拋出該類(lèi)型異常的方法,自然可以拋出任意類(lèi)型的異常。也就是說(shuō),當(dāng)一個(gè)方法聲明為 “throws Exception” 時(shí),它可以拋出任何類(lèi)型的異常。注意,因?yàn)檫@種聲明方式會(huì)給方法的調(diào)用者帶來(lái)困擾,所以我們盡量不要這么做。
接下來(lái)請(qǐng)看回顧一下示例 3:
現(xiàn)在回顧一下示例 3 中 catch(IOException e) {...} 部分。如果將它改為 catch(Exception e) {...},就表示捕獲所有類(lèi)型的異常,即它前面的 try 部分不論拋出什么類(lèi)型的異常,它都可以處理。某些情況下這樣做是合理的,因?yàn)槲覀儾幌M?dāng)遇到某個(gè)特定種類(lèi)的異常時(shí),程序終止運(yùn)行。
Java 異常入門(mén)(2/2)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/64180.html
摘要:創(chuàng)建和拋出異常對(duì)象當(dāng)我們的某個(gè)方法執(zhí)行當(dāng)中遇到錯(cuò)誤,無(wú)法繼續(xù)處理的時(shí)候,我們也可以自己創(chuàng)建異常對(duì)象并拋出,交給方法的調(diào)用者處理。拋出異常的方式是使用關(guān)鍵字。如果日期字符串不符合格式,則拋出一個(gè)異常。 創(chuàng)建和拋出異常對(duì)象 當(dāng)我們的某個(gè)方法執(zhí)行當(dāng)中遇到錯(cuò)誤,無(wú)法繼續(xù)處理的時(shí)候,我們也可以自己創(chuàng)建異常對(duì)象并拋出,交給方法的調(diào)用者處理。拋出異常的方式是使用 throw 關(guān)鍵字。下面是一個(gè)例子...
摘要:聲明本文所有列舉的問(wèn)題都來(lái)源于編程隨想的博客,這個(gè)博客的博主知識(shí)淵博,編程方面的一些文章質(zhì)量很高,給人醍醐灌頂?shù)母杏X(jué)。 聲明:本文所有列舉的問(wèn)題都來(lái)源于 《編程隨想》的博客,這個(gè)博客的博主知識(shí)淵博,編程方面的一些文章質(zhì)量很高,給人醍醐灌頂?shù)母杏X(jué)。 算法和數(shù)據(jù)結(jié)構(gòu) 什么時(shí)候該用數(shù)組類(lèi)型容器,什么時(shí)候該用鏈表型容器,如何合理的使用數(shù)據(jù)類(lèi)型 什么是散列函數(shù),HashMap的實(shí)現(xiàn)原理是什么 ...
摘要:工具可以報(bào)告兩種問(wèn)題警告和錯(cuò)誤。警告只是說(shuō)明代碼可能無(wú)法正常工作,但不會(huì)阻止程序執(zhí)行。中的是一組無(wú)序的集合。其中來(lái)指定異常類(lèi)型,來(lái)捕獲異常對(duì)象。其中代表只導(dǎo)入指定的部分,代表除了指定的部分都導(dǎo)入。 本文首發(fā)于微信公眾號(hào)「劉望舒」 前言 Dart是Flutter SDK指定的語(yǔ)言,因此要學(xué)習(xí)Flutter,Dart是必須掌握的。關(guān)于Dart可以寫(xiě)一本書(shū)了,這里用一篇文章來(lái)介紹下Dart的精...
摘要:包含了支持服務(wù)開(kāi)發(fā)的類(lèi),并為提供基礎(chǔ),如語(yǔ)言基礎(chǔ)操作操作網(wǎng)絡(luò)通信以及多線程等技術(shù)。在運(yùn)行文件時(shí),的解釋器對(duì)這些字節(jié)碼進(jìn)行解釋執(zhí)行,執(zhí)行過(guò)程中需要加入的類(lèi)在連接階段被載入到運(yùn)行環(huán)境中。支持多個(gè)線程同時(shí)執(zhí)行,并提供多線程之間的同步機(jī)制。 1.什么是Java語(yǔ)言 簡(jiǎn)單地說(shuō),Java 是由 Sun Microsystems 公司于 1995 年推出的一門(mén)面向?qū)ο蟪绦蛟O(shè)計(jì)語(yǔ)言。2009 年 Or...
閱讀 964·2019-08-30 15:55
閱讀 557·2019-08-26 13:56
閱讀 2090·2019-08-26 12:23
閱讀 3310·2019-08-26 10:29
閱讀 610·2019-08-26 10:17
閱讀 2878·2019-08-23 16:53
閱讀 708·2019-08-23 15:55
閱讀 2832·2019-08-23 14:25