您好,登錄后才能下訂單哦!
這篇文章主要介紹“怎么理解java中的Exception和Error”,在日常操作中,相信很多人在怎么理解java中的Exception和Error問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”怎么理解java中的Exception和Error”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
在 Java 中的基本理念是 結(jié)構(gòu)不佳的代碼不能運(yùn)行,發(fā)現(xiàn)錯(cuò)誤的理想時(shí)期是在編譯期間,因?yàn)槟悴挥眠\(yùn)行程序,只是憑借著對(duì) Java 基本理念的理解就能發(fā)現(xiàn)問(wèn)題。但是編譯期并不能找出所有的問(wèn)題,有一些 NullPointerException 和 ClassNotFoundException 在編譯期找不到,這些異常是 RuntimeException 運(yùn)行時(shí)異常,這些異常往往在運(yùn)行時(shí)才能被發(fā)現(xiàn)。
我們寫(xiě) Java 程序經(jīng)常會(huì)出現(xiàn)兩種問(wèn)題,一種是 java.lang.Exception ,一種是 java.lang.Error,都用來(lái)表示出現(xiàn)了異常情況,下面就針對(duì)這兩種概念進(jìn)行理解。
認(rèn)識(shí) ExceptionException 位于 java.lang 包下,它是一種頂級(jí)接口,繼承于 Throwable 類,Exception 類及其子類都是 Throwable 的組成條件,是程序出現(xiàn)的合理情況。
在認(rèn)識(shí) Exception 之前,有必要先了解一下什么是 Throwable。
什么是 Throwable
Throwable 類是 Java 語(yǔ)言中所有錯(cuò)誤(errors)和異常(exceptions)的父類。只有繼承于 Throwable 的類或者其子類才能夠被拋出,還有一種方式是帶有 Java 中的 @throw 注解的類也可以拋出。
在Java規(guī)范中,對(duì)非受查異常和受查異常的定義是這樣的:
“The unchecked exception classes are the run-time exception classes and the error classes.
“The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are Throwable and all its subclasses other than RuntimeException and its subclasses and Errorand its subclasses.
也就是說(shuō),除了 RuntimeException 和其子類,以及error和其子類,其它的所有異常都是 checkedException。
那么,按照這種邏輯關(guān)系,我們可以對(duì) Throwable 及其子類進(jìn)行歸類分析。
可以看到,Throwable 位于異常和錯(cuò)誤的最頂層,我們查看 Throwable 類中發(fā)現(xiàn)它的方法和屬性有很多,我們只討論其中幾個(gè)比較常用的。
// 返回拋出異常的詳細(xì)信息 public string getMessage(); public string getLocalizedMessage(); //返回異常發(fā)生時(shí)的簡(jiǎn)要描述 public public String toString(); // 打印異常信息到標(biāo)準(zhǔn)輸出流上 public void printStackTrace(); public void printStackTrace(PrintStream s); public void printStackTrace(PrintWriter s) // 記錄棧幀的的當(dāng)前狀態(tài) public synchronized Throwable fillInStackTrace();
此外,因?yàn)?Throwable 的父類也是 Object,所以常用的方法還有繼承其父類的getClass() 和getName() 方法。
常見(jiàn)的 Exception
下面我們回到 Exception 的探討上來(lái),現(xiàn)在你知道了 Exception 的父類是 Throwable,并且 Exception 有兩種異常,一種是 RuntimeException ;一種是 CheckedException,這兩種異常都應(yīng)該去捕獲。
下面列出了一些 Java 中常見(jiàn)的異常及其分類,這塊面試官也可能讓你舉出幾個(gè)常見(jiàn)的異常情況并將其分類。
RuntimeException
序號(hào)異常名稱異常描述
序號(hào) | 異常名稱 | 異常描述 |
---|---|---|
1 | ArrayIndexOutOfBoundsException | 數(shù)組越界異常 |
2 | NullPointerException | 空指針異常 |
3 | IllegalArgumentException | 非法參數(shù)異常 |
4 | NegativeArraySizeException | 數(shù)組長(zhǎng)度為負(fù)異常 |
5 | IllegalStateException | 非法狀態(tài)異常 |
6 | ClassCastException | 類型轉(zhuǎn)換異常 |
UncheckedException
序號(hào) | 異常名稱 | 異常描述 |
---|---|---|
1 | NoSuchFieldException | 表示該類沒(méi)有指定名稱拋出來(lái)的異常 |
2 | NoSuchMethodException | 表示該類沒(méi)有指定方法拋出來(lái)的異常 |
3 | IllegalAccessException | 不允許訪問(wèn)某個(gè)類的異常 |
4 | ClassNotFoundException | 類沒(méi)有找到拋出異常 |
與 Exception 有關(guān)的 Java 關(guān)鍵字
那么 Java 中是如何處理這些異常的呢?在 Java 中有這幾個(gè)關(guān)鍵字 throws、throw、try、finally、catch 下面我們分別來(lái)探討一下
throws 和 throw
在 Java 中,異常也就是一個(gè)對(duì)象,它能夠被程序員自定義拋出或者應(yīng)用程序拋出,必須借助于 throws 和 throw 語(yǔ)句來(lái)定義拋出異常。
throws 和 throw 通常是成對(duì)出現(xiàn)的,例如
static void cacheException() throws Exception{ throw new Exception(); }
throw 語(yǔ)句用在方法體內(nèi),表示拋出異常,由方法體內(nèi)的語(yǔ)句處理。throws 語(yǔ)句用在方法聲明后面,表示再拋出異常,由該方法的調(diào)用者來(lái)處理。
throws 主要是聲明這個(gè)方法會(huì)拋出這種類型的異常,使它的調(diào)用者知道要捕獲這個(gè)異常。throw 是具體向外拋異常的動(dòng)作,所以它是拋出一個(gè)異常實(shí)例。
try 、finally 、catch
這三個(gè)關(guān)鍵字主要有下面幾種組合方式 try...catch 、try...finally、try...catch...finally。
try...catch 表示對(duì)某一段代碼可能拋出異常進(jìn)行的捕獲,如下
static void cacheException() throws Exception{ try { System.out.println("1"); }catch (Exception e){ e.printStackTrace(); } }
try...finally 表示對(duì)一段代碼不管執(zhí)行情況如何,都會(huì)走 finally 中的代碼
static void cacheException() throws Exception{ for (int i = 0; i < 5; i++) { System.out.println("enter: i=" + i); try { System.out.println("execute: i=" + i); continue; } finally { System.out.println("leave: i=" + i); } } }
try...catch...finally 也是一樣的,表示對(duì)異常捕獲后,再走 finally 中的代碼邏輯。
JDK1.7 使用 try...with...resources 優(yōu)雅關(guān)閉資源
Java 類庫(kù)中有許多資源需要通過(guò) close 方法進(jìn)行關(guān)閉。比如 InputStream、OutputStream,數(shù)據(jù)庫(kù)連接對(duì)象 Connection,MyBatis 中的 SqlSession 會(huì)話等。作為開(kāi)發(fā)人員經(jīng)常會(huì)忽略掉資源的關(guān)閉方法,導(dǎo)致內(nèi)存泄漏。
根據(jù)經(jīng)驗(yàn),try-finally語(yǔ)句是確保資源會(huì)被關(guān)閉的最佳方法,就算異常或者返回也一樣。try-catch-finally 一般是這樣來(lái)用的
static String firstLineOfFile(String path) throws IOException { BufferedReader br = new BufferedReader(new FileReader(path)); try { return br.readLine(); }finally { br.close(); } }
這樣看起來(lái)代碼還是比較整潔,但是當(dāng)我們添加第二個(gè)需要關(guān)閉的資源的時(shí)候,就像下面這樣
static void copy(String src,String dst) throws Exception{ InputStream is = new FileInputStream(src); try { OutputStream os = new FileOutputStream(dst); try { byte[] buf = new byte[100]; int n; while ((n = is.read()) >= 0){ os.write(buf,n,0); } }finally { os.close(); } }finally { is.close(); } }
這樣感覺(jué)這個(gè)方法已經(jīng)變得臃腫起來(lái)了。
而且這種寫(xiě)法也存在諸多問(wèn)題,即使 try - finally 能夠正確關(guān)閉資源,但是它不能阻止異常的拋出,因?yàn)?try 和 finally 塊中都可能有異常的發(fā)生。
比如說(shuō)你正在讀取的時(shí)候硬盤損壞,這個(gè)時(shí)候你就無(wú)法讀取文件和關(guān)閉資源了,此時(shí)會(huì)拋出兩個(gè)異常。但是在這種情況下,第二個(gè)異常會(huì)抹掉第一個(gè)異常。在異常堆棧中也無(wú)法找到第一個(gè)異常的記錄,怎么辦,難道像這樣來(lái)捕捉異常么?
static void tryThrowException(String path) throws Exception { BufferedReader br = new BufferedReader(new FileReader(path)); try { String s = br.readLine(); System.out.println("s = " + s); }catch (Exception e){ e.printStackTrace(); }finally { try { br.close(); }catch (Exception e){ e.printStackTrace(); }finally { br.close(); } } }
這種寫(xiě)法,雖然能解決異常拋出的問(wèn)題,但是各種 try-cath-finally 的嵌套會(huì)讓代碼變得非常臃腫。
Java7 中引入了try-with-resources 語(yǔ)句時(shí),所有這些問(wèn)題都能得到解決。要使用 try-with-resources 語(yǔ)句,首先要實(shí)現(xiàn) AutoCloseable 接口,此接口包含了單個(gè)返回的 close 方法。Java 類庫(kù)與三方類庫(kù)中的許多類和接口,現(xiàn)在都實(shí)現(xiàn)或者擴(kuò)展了 AutoCloseable 接口。如果編寫(xiě)了一個(gè)類,它代表的是必須關(guān)閉的資源,那么這個(gè)類應(yīng)該實(shí)現(xiàn) AutoCloseable 接口。
java 引入了 try-with-resources 聲明,將 try-catch-finally 簡(jiǎn)化為 try-catch,這其實(shí)是一種語(yǔ)法糖,在編譯時(shí)會(huì)進(jìn)行轉(zhuǎn)化為 try-catch-finally 語(yǔ)句。
下面是使用 try-with-resources 的第一個(gè)范例
/** * 使用try-with-resources 改寫(xiě)示例一 * @param path * @return * @throws IOException */ static String firstLineOfFileAutoClose(String path) throws IOException { try(BufferedReader br = new BufferedReader(new FileReader(path))){ return br.readLine(); } }
使用 try-with-resources 改寫(xiě)程序的第二個(gè)示例
static void copyAutoClose(String src,String dst) throws IOException{ try(InputStream in = new FileInputStream(src); OutputStream os = new FileOutputStream(dst)){ byte[] buf = new byte[1000]; int n; while ((n = in.read(buf)) >= 0){ os.write(buf,0,n); } } }
使用 try-with-resources 不僅使代碼變得通俗易懂,也更容易診斷。以firstLineOfFileAutoClose方法為例,如果調(diào)用 readLine()和 close() 方法都拋出異常,后一個(gè)異常就會(huì)被禁止,以保留第一個(gè)異常。(公號(hào)回復(fù) 高效 即可領(lǐng)取 Effective Java 第三版中文 pdf)
異常處理的原則
我們?cè)谌粘L幚懋惓5拇a中,應(yīng)該遵循三個(gè)原則:
不要捕獲類似 Exception 之類的異常,而應(yīng)該捕獲類似特定的異常,比如 InterruptedException,方便排查問(wèn)題,而且也能夠讓其他人接手你的代碼時(shí),會(huì)減少罵你的次數(shù)。
不要生吞異常。這是異常處理中要特別注重的事情。如果我們不把異常拋出來(lái),或者也沒(méi)有輸出到 Logger 日志中,程序可能會(huì)在后面以不可控的方式結(jié)束。
不要在函數(shù)式編程中使用 checkedException。
什么是 Error
Error 是程序無(wú)法處理的錯(cuò)誤,表示運(yùn)行應(yīng)用程序中較嚴(yán)重問(wèn)題。大多數(shù)錯(cuò)誤與代碼編寫(xiě)者執(zhí)行的操作無(wú)關(guān),而表示代碼運(yùn)行時(shí) JVM(Java 虛擬機(jī))出現(xiàn)的問(wèn)題。這些錯(cuò)誤是不可檢查的,因?yàn)樗鼈冊(cè)趹?yīng)用程序的控制和處理能力之 外,而且絕大多數(shù)是程序運(yùn)行時(shí)不允許出現(xiàn)的狀況,比如 OutOfMemoryError 和 StackOverflowError異常的出現(xiàn)會(huì)有幾種情況,這里需要先介紹一下 Java 內(nèi)存模型 JDK1.7。
其中包括兩部分,由所有線程共享的數(shù)據(jù)區(qū)和線程隔離的數(shù)據(jù)區(qū)組成,在上面的 Java 內(nèi)存模型中,只有程序計(jì)數(shù)器是不會(huì)發(fā)生 OutOfMemoryError 情況的區(qū)域,程序計(jì)數(shù)器控制著計(jì)算機(jī)指令的分支、循環(huán)、跳轉(zhuǎn)、異常處理和線程恢復(fù),并且程序計(jì)數(shù)器是每個(gè)線程私有的。
“什么是線程私有:表示的就是各條線程之間互不影響,獨(dú)立存儲(chǔ)的內(nèi)存區(qū)域。
如果應(yīng)用程序執(zhí)行的是 Java 方法,那么這個(gè)計(jì)數(shù)器記錄的就是虛擬機(jī)字節(jié)碼指令的地址;如果正在執(zhí)行的是 Native 方法,這個(gè)計(jì)數(shù)器值則為空(Undefined)。
除了程序計(jì)數(shù)器外,其他區(qū)域:方法區(qū)(Method Area)、虛擬機(jī)棧(VM Stack)、本地方法棧(Native Method Stack) 和 堆(Heap) 都是可能發(fā)生 OutOfMemoryError 的區(qū)域。
虛擬機(jī)棧:如果線程請(qǐng)求的棧深度大于虛擬機(jī)棧所允許的深度,將會(huì)出現(xiàn) StackOverflowError 異常;如果虛擬機(jī)動(dòng)態(tài)擴(kuò)展無(wú)法申請(qǐng)到足夠的內(nèi)存,將出現(xiàn) OutOfMemoryError。
本地方法棧和虛擬機(jī)棧一樣
堆:Java 堆可以處于物理上不連續(xù),邏輯上連續(xù),就像我們的磁盤空間一樣,如果堆中沒(méi)有內(nèi)存完成實(shí)例分配,并且堆無(wú)法擴(kuò)展時(shí),將會(huì)拋出 OutOfMemoryError。
方法區(qū):方法區(qū)無(wú)法滿足內(nèi)存分配需求時(shí),將拋出 OutOfMemoryError 異常。
一道經(jīng)典的面試題
一道非常經(jīng)典的面試題,NoClassDefFoundError 和 ClassNotFoundException 有什么區(qū)別?
在類的加載過(guò)程中, JVM 或者 ClassLoader 無(wú)法找到對(duì)應(yīng)的類時(shí),都可能會(huì)引起這兩種異常/錯(cuò)誤,由于不同的 ClassLoader 會(huì)從不同的地方加載類,有時(shí)是錯(cuò)誤的 CLASSPATH 類路徑導(dǎo)致的這類錯(cuò)誤,有時(shí)是某個(gè)庫(kù)的 jar 包缺失引發(fā)這類錯(cuò)誤。NoClassDefFoundError 表示這個(gè)類在編譯時(shí)期存在,但是在運(yùn)行時(shí)卻找不到此類,有時(shí)靜態(tài)初始化塊也會(huì)導(dǎo)致 NoClassDefFoundError 錯(cuò)誤。
“ClassLoader 是類路徑裝載器,在Java 中,類路徑裝載器一共有三種兩類一種是虛擬機(jī)自帶的 ClassLoader,分為三種
啟動(dòng)類加載器(Bootstrap) ,負(fù)責(zé)加載 $JAVAHOME/jre/lib/rt.jar
擴(kuò)展類加載器(Extension),負(fù)責(zé)加載 $JAVAHOME/jre/lib/ext/*.jar
應(yīng)用程序類加載器(AppClassLoader),加載當(dāng)前應(yīng)用的 classpath 的所有類
第二種是用戶自定義類加載器
Java.lang.ClassLoader 的子類,用戶可以定制類的加載方式。
另一方面,ClassNotFoundException 與編譯時(shí)期無(wú)關(guān),當(dāng)你嘗試在運(yùn)行時(shí)使用反射加載類時(shí),ClassNotFoundException 就會(huì)出現(xiàn)。
簡(jiǎn)而言之,ClassNotFoundException 和 NoClassDefFoundError 都是由 CLASSPATH 中缺少類引起的,通常是由于缺少 JAR 文件而引起的,但是如果 JVM 認(rèn)為應(yīng)用運(yùn)行時(shí)找不到相應(yīng)的引用,就會(huì)拋出 NoClassDefFoundError 錯(cuò)誤;當(dāng)你在代碼中顯示的加載類比如 Class.forName() 調(diào)用時(shí)卻沒(méi)有找到相應(yīng)的類,就會(huì)拋出 java.lang.ClassNotFoundException。
NoClassDefFoundError 是 JVM 引起的錯(cuò)誤,是 unchecked,未經(jīng)檢查的。因此不會(huì)使用 try-catch 或者 finally 語(yǔ)句塊;另外,ClassNotFoundException 是受檢異常,因此需要 try-catch 語(yǔ)句塊或者 try-finally 語(yǔ)句塊包圍,否則會(huì)導(dǎo)致編譯錯(cuò)誤。
調(diào)用 Class.forName()、ClassLoader.findClass() 和 ClassLoader.loadClass() 等方法時(shí)可能會(huì)引起 java.lang.ClassNotFoundException,如圖所示
NoClassDefFoundError 是鏈接錯(cuò)誤,發(fā)生在鏈接階段,當(dāng)解析引用找不到對(duì)應(yīng)的類,就會(huì)觸發(fā);而 ClassNotFoundException 是發(fā)生在運(yùn)行時(shí)的異常。
到此,關(guān)于“怎么理解java中的Exception和Error”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。