java 異常處理
異常是指程序中的一些錯誤,但并不是所有的錯誤都是異常。在程序設(shè)計中,進行異常處理是非常關(guān)鍵和重要的一部分。java提供了try、catch、finally這三個關(guān)鍵字來處理異常。
1. 異常發(fā)生的原因
異常發(fā)生的原因有很多,通常包含以下幾大類:
- 用戶輸入了非法數(shù)據(jù)。
- 要打開的文件不存在。
- 網(wǎng)絡(luò)通信時連接中斷,或者jvm內(nèi)存溢出。
這些異常有的是因為用戶錯誤引起,有的是程序錯誤引起的,還有其它一些是因為物理錯誤引起的。
2. 異常類型
為了更好地處理 java 異常,我們需要掌握以下三種類型的異常:
- 檢查性異常:
最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程序員無法預(yù)見的。例如要打開一個不存在文件時,一個異常就發(fā)生了,這些異常在編譯時不能被簡單地忽略。 - 運行時異常:
運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常可以在編譯時被忽略。 - 錯誤:
錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中通常被忽略。例如,當(dāng)棧溢出時,一個錯誤就發(fā)生了,它們在編譯也檢查不到的。
3. exception 類的層次
所有的異常類是從 java.lang.exception 類繼承的子類。
exception 類是 throwable 類的子類。除了exception類外,throwable還有一個子類error 。
java 程序通常不捕獲錯誤。錯誤一般發(fā)生在嚴(yán)重故障時,它們在java程序處理的范疇之外。
error 用來指示運行時環(huán)境發(fā)生的錯誤。
例如,jvm 內(nèi)存溢出。一般地,程序不會從錯誤中恢復(fù)。
異常類有兩個主要的子類:ioexception 類和 runtimeexception 類。
在 java 內(nèi)置類中(接下來會說明),有大部分常用檢查性和非檢查性異常。
4. java 內(nèi)置異常類
java 語言定義了一些異常類在 java.lang 標(biāo)準(zhǔn)包中。
標(biāo)準(zhǔn)運行時異常類的子類是最常見的異常類。由于 java.lang 包是默認(rèn)加載到所有的 java 程序的,所以大部分從運行時異常類繼承而來的異常都可以直接使用。
java 根據(jù)各個類庫也定義了一些其他的異常,下面的表中列出了 java 的非檢查性異常。
異常 | 描述 |
---|---|
arithmeticexception | 當(dāng)出現(xiàn)異常的運算條件時,拋出此異常。例如,一個整數(shù)"除以零"時,拋出此類的一個范例。 |
arrayindexoutofboundsexception | 用非法索引訪問數(shù)組時拋出的異常。如果索引為負(fù)或大于等于數(shù)組大小,則該索引為非法索引。 |
arraystoreexception | 試圖將錯誤類型的對象存儲到一個對象數(shù)組時拋出的異常。 |
classcastexception | 當(dāng)試圖將對象強制轉(zhuǎn)換為不是范例的子類時,拋出該異常。 |
illegalargumentexception | 拋出的異常表明向方法傳遞了一個不合法或不正確的參數(shù)。 |
illegalmonitorstateexception | 拋出的異常表明某一線程已經(jīng)試圖等待對象的監(jiān)視器,或者試圖通知其他正在等待對象的監(jiān)視器而本身沒有指定監(jiān)視器的線程。 |
illegalstateexception | 在非法或不適當(dāng)?shù)臅r間調(diào)用方法時產(chǎn)生的信號。換句話說,即 java 環(huán)境或 java 應(yīng)用程序沒有處于請求操作所要求的適當(dāng)狀態(tài)下。 |
illegalthreadstateexception | 線程沒有處于請求操作所要求的適當(dāng)狀態(tài)時拋出的異常。 |
indexoutofboundsexception | 指示某排序索引(例如對數(shù)組、字符串或向量的排序)超出范圍時拋出。 |
negativearraysizeexception | 如果應(yīng)用程序試圖創(chuàng)建大小為負(fù)的數(shù)組,則拋出該異常。 |
nullpointerexception | 當(dāng)應(yīng)用程序試圖在需要對象的地方使用 null 時,拋出該異常 |
numberformatexception | 當(dāng)應(yīng)用程序試圖將字符串轉(zhuǎn)換成一種數(shù)值類型,但該字符串不能轉(zhuǎn)換為適當(dāng)格式時,拋出該異常。 |
securityexception | 由安全管理器拋出的異常,指示存在安全侵犯。 |
stringindexoutofboundsexception | 此異常由 string 方法拋出,指示索引或者為負(fù),或者超出字符串的大小。 |
unsupportedoperationexception | 當(dāng)不支持請求的操作時,拋出該異常。 |
下面的表中列出了 java 定義在 java.lang 包中的檢查性異常類。
異常 | 描述 |
---|---|
classnotfoundexception | 應(yīng)用程序試圖加載類時,找不到相應(yīng)的類,拋出該異常。 |
clonenotsupportedexception | 當(dāng)調(diào)用 object 類中的 clone 方法克隆對象,但該對象的類無法實現(xiàn) cloneable 接口時,拋出該異常。 |
illegalaccessexception | 拒絕訪問一個類的時候,拋出該異常。 |
instantiationexception | 當(dāng)試圖使用 class 類中的 newinstance 方法創(chuàng)建一個類的范例,而指定的類對象因為是一個接口或是一個抽象類而無法范例化時,拋出該異常。 |
interruptedexception | 一個線程被另一個線程中斷,拋出該異常。 |
nosuchfieldexception | 請求的變量不存在 |
nosuchmethodexception | 請求的方法不存在 |
5. 異常方法
下面的列表是 throwable 類的主要方法:
序號 | 方法及說明 |
---|---|
1 |
public string getmessage() 返回關(guān)于發(fā)生的異常的詳細信息。這個消息在throwable 類的構(gòu)造函數(shù)中初始化了。 |
2 |
public throwable getcause() 返回一個throwable 對象代表異常原因。 |
3 |
public string tostring() 使用getmessage()的結(jié)果返回類的串級名字。 |
4 |
public void printstacktrace() 打印tostring()結(jié)果和棧層次到system.err,即錯誤輸出流。 |
5 |
public stacktraceelement [] getstacktrace() 返回一個包含堆棧層次的數(shù)組。下標(biāo)為0的元素代表棧頂,最后一個元素代表方法調(diào)用堆棧的棧底。 |
6 |
public throwable fillinstacktrace() 用當(dāng)前的調(diào)用棧層次填充throwable 對象棧層次,添加到棧層次任何先前信息中。 |
6. 捕獲異常
使用 try 和 catch 關(guān)鍵字可以捕獲異常。try/catch 代碼塊放在異常可能發(fā)生的地方。
try/catch代碼塊中的代碼稱為保護代碼,使用 try/catch 的語法如下:
try { // 程序代碼 }catch(exceptionname e1) { //catch 塊 }
catch 語句包含要捕獲異常類型的聲明。當(dāng)保護代碼塊中發(fā)生一個異常時,try 后面的 catch 塊就會被檢查。
如果發(fā)生的異常包含在 catch 塊中,異常會被傳遞到該 catch 塊,這和傳遞一個參數(shù)到方法是一樣。
范例
下面的例子中聲明有兩個元素的一個數(shù)組,當(dāng)代碼試圖訪問數(shù)組的第三個元素的時候就會拋出一個異常。
// 文件名 : exceptest.java import java.io.*; public class exceptest{ public static void main(string args[]){ try{ int a[] = new int[2]; system.out.println("access element three :" + a[3]); }catch(arrayindexoutofboundsexception e){ system.out.println("exception thrown :" + e); } system.out.println("out of the block"); } }
以上代碼編譯運行輸出結(jié)果如下:
exception thrown :java.lang.arrayindexoutofboundsexception: 3 out of the block
7. 多重捕獲塊
一個 try 代碼塊后面跟隨多個 catch 代碼塊的情況就叫多重捕獲。
多重捕獲塊的語法如下所示:
try{ // 程序代碼 }catch(異常類型1 異常的變量名1){ // 程序代碼 }catch(異常類型2 異常的變量名2){ // 程序代碼 }catch(異常類型3 異常的變量名3){ // 程序代碼 }
上面的代碼段包含了 3 個 catch塊。
可以在 try 語句后面添加任意數(shù)量的 catch 塊。
如果保護代碼中發(fā)生異常,異常被拋給第一個 catch 塊。
如果拋出異常的數(shù)據(jù)類型與 exceptiontype1 匹配,它在這里就會被捕獲。
如果不匹配,它會被傳遞給第二個 catch 塊。
如此,直到異常被捕獲或者通過所有的 catch 塊。
范例
該范例展示了怎么使用多重 try/catch。
try { file = new fileinputstream(filename); x = (byte) file.read(); } catch(filenotfoundexception f) { // not valid! f.printstacktrace(); return -1; } catch(ioexception i) { i.printstacktrace(); return -1; }
8. throws/throw 關(guān)鍵字:
如果一個方法沒有捕獲到一個檢查性異常,那么該方法必須使用 throws 關(guān)鍵字來聲明。throws 關(guān)鍵字放在方法簽名的尾部。
也可以使用 throw 關(guān)鍵字拋出一個異常,無論它是新范例化的還是剛捕獲到的。
下面方法的聲明拋出一個 remoteexception 異常:
import java.io.*; public class classname { public void deposit(double amount) throws remoteexception { // method implementation throw new remoteexception(); } //remainder of class definition }
一個方法可以聲明拋出多個異常,多個異常之間用逗號隔開。
例如,下面的方法聲明拋出 remoteexception 和 insufficientfundsexception:
import java.io.*; public class classname { public void withdraw(double amount) throws remoteexception, insufficientfundsexception { // method implementation } //remainder of class definition }
9. finally關(guān)鍵字
finally 關(guān)鍵字用來創(chuàng)建在 try 代碼塊后面執(zhí)行的代碼塊。
無論是否發(fā)生異常,finally 代碼塊中的代碼總會被執(zhí)行。
在 finally 代碼塊中,可以運行清理類型等收尾善后性質(zhì)的語句。
finally 代碼塊出現(xiàn)在 catch 代碼塊最后,語法如下:
try { // 程序代碼 }catch(異常類型1 異常的變量名1){ // 程序代碼 }catch(異常類型2 異常的變量名2){ // 程序代碼 }finally{ // 程序代碼 }
范例
public class exceptest{ public static void main(string args[]){ int a[] = new int[2]; try{ system.out.println("access element three :" + a[3]); }catch(arrayindexoutofboundsexception e){ system.out.println("exception thrown :" + e); } finally{ a[0] = 6; system.out.println("first element value: " +a[0]); system.out.println("the finally statement is executed"); } } }
以上范例編譯運行結(jié)果如下:
exception thrown :java.lang.arrayindexoutofboundsexception: 3 first element value: 6 the finally statement is executed
注意下面事項:
- catch 不能獨立于 try 存在。
- 在 try/catch 后面添加 finally 塊并非強制性要求的。
- try 代碼后不能既沒 catch 塊也沒 finally 塊。
- try, catch, finally 塊之間不能添加任何代碼。
10. 聲明自定義異常
在 java 中你可以自定義異常。編寫自己的異常類時需要記住下面的幾點。
- 所有異常都必須是 throwable 的子類。
- 如果希望寫一個檢查性異常類,則需要繼承 exception 類。
- 如果你想寫一個運行時異常類,那么需要繼承 runtimeexception 類。
可以像下面這樣定義自己的異常類:
class myexception extends exception{ }
只繼承exception 類來創(chuàng)建的異常類是檢查性異常類。
下面的 insufficientfundsexception 類是用戶定義的異常類,它繼承自 exception。
一個異常類和其它任何類一樣,包含有變量和方法。
范例
以下范例是一個銀行賬戶的模擬,通過銀行卡的號碼完成識別,可以進行存錢和取錢的操作。
// 文件名insufficientfundsexception.java import java.io.*; //自定義異常類,繼承exception類 public class insufficientfundsexception extends exception { //此處的amount用來儲存當(dāng)出現(xiàn)異常(取出錢多于余額時)所缺乏的錢 private double amount; public insufficientfundsexception(double amount) { this.amount = amount; } public double getamount() { return amount; } }
為了展示如何使用我們自定義的異常類,
在下面的 checkingaccount 類中包含一個 withdraw() 方法拋出一個 insufficientfundsexception 異常。
// 文件名稱 checkingaccount.java import java.io.*; //此類模擬銀行賬戶 public class checkingaccount { //balance為余額,number為卡號 private double balance; private int number; public checkingaccount(int number) { this.number = number; } //方法:存錢 public void deposit(double amount) { balance += amount; } //方法:取錢 public void withdraw(double amount) throws insufficientfundsexception { if(amount