異常處理機制
在編寫程式時,經常要在可能出現錯誤的地方加上檢測的代碼,如進行x/y運算時,要檢測分母為0,資料為空,輸入的不是資料而是字元等。過多的分支會導緻程式的代碼加長,可讀性差。是以采用異常機制。
Java采用異常處理機制,将異常處理的程式代碼集中在一起,與正常的程式代碼分開,使得程式簡潔,并易于維護。
Java提供的是異常處理的抓抛模型。
Java程式的執行過程中如出現異常,會生成一個異常類對象,該異常對象将被送出給Java運作時系統,這個過程稱為抛出(throw)異常。
異常對象的生成
由虛拟機自動生成:程式運作過程中,虛拟機檢測到程式發生了問題,如果在目前代碼中沒有找到相應的處理程式,就會在背景自動建立一個對應異常類的執行個體對象并抛出——自動抛出。
由開發人員手動建立:Exception exception = new ClassCastException();——建立好的異常對象不抛出對程式沒有任何影響,和建立一個普通對象一樣。
如果一個方法内抛出異常,該異常對象會被抛給調用者方法中處理。如果異常沒有在調用者方法中處理,它繼續被抛給這個調用方法的上層方法。這個過程将一直繼續下去,直到異常被處理。這一過程稱為捕獲(catch)異常。
如果一個異常回到main()方法,并且main()也不處理,則程式運作終止。
程式員通常隻能處理Exception,而對Error無能為力。
捕獲異常
使用 try 和 catch 關鍵字可以捕獲異常。try/catch 代碼塊放在異常可能發生的地方。try/catch代碼塊中的代碼稱為保護代碼。使用try/catch的文法如下:
try {
// 程式代碼
}catch(ExceptionName e) {
//Catch 塊
}
try
捕獲異常的第一步是用try{…}語句塊標明捕獲異常的範圍,将可能出現異常的代碼放在try語句塊中。
catch (Exceptiontype e)
Catch 語句包含要捕獲異常類型的聲明。當保護代碼塊中發生一個異常時,try 後面的 catch 塊就會被檢查。如果發生的異常包含在 catch 塊中,異常會被傳遞到該 catch 塊,這和傳遞一個參數到方法是一樣。
在catch語句塊中是對異常對象進行處理的代碼。每個try語句塊可以伴随一個或多個catch語句,用于處理可能産生的不同類型的異常對象。
如果明确知道産生的是何種異常,可以用該異常類作為catch的參數,也可以用其父類作為catch的參數。比如:可以用ArithmeticException類作為參數的地方,就可以用RuntimeException類作為參數,或者用所有異常的父類Exception類作為參數。但不能是與ArithmeticException類無關的異常,如NullPointerException(catch中的語句将不會執行)。
捕獲異常的有關資訊:與其它對象一樣,可以通路一個異常對象的成員變量或調用它的方法。
getMessage() 擷取異常資訊,傳回字元串
printStackTrace() 擷取異常類名和異常資訊,以及異常出現在程式中的位置。傳回值void。
下面的例子中聲明有兩個元素的一個數組,當代碼試圖通路數組的第三個元素的時候就會抛出一個異常。
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
try{
// 可能産生異常的代碼,即需要被檢測的代碼
int a[] = new int[2];
System.out.println("擷取數組的第三個元素:" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){ // e用于接收try檢測到的異常對象
System.out.println("抛出的異常:" + e);
System.out.println("message:"+e.getMessage()); // 擷取的是異常的資訊
System.out.println("toString:"+e.toString()); // 擷取的是異常的名字+異常的資訊
e.printStackTrace(); // 列印異常在堆棧中資訊;異常名稱+異常資訊+異常的位置
}
System.out.println("try/catch代碼塊之後");
}
}
執行結果為:
抛出的異常:java.lang.ArrayIndexOutOfBoundsException: 3
message:3
toString:java.lang.ArrayIndexOutOfBoundsException: 3
java.lang.ArrayIndexOutOfBoundsException: 3
at ExcepTest.main(ExcepTest.java:6)
try/catch代碼塊之後
多重捕獲塊
一個 try 代碼塊後面跟随多個 catch 代碼塊的情況就叫多重捕獲。
多重捕獲塊的文法如下所示:
try{
// 程式代碼
}catch(異常類型1 異常的變量名1){
// 程式代碼
}catch(異常類型2 異常的變量名2){
// 程式代碼
}catch(異常類型2 異常的變量名2){
// 程式代碼
}
上面的代碼段包含了 3 個 catch塊。可以在 try 語句後面添加任意數量的 catch 塊。
如果保護代碼中發生異常,異常被抛給第一個 catch 塊。如果抛出異常的資料類型與 異常類型1 比對,它在這裡就會被捕獲。如果不比對,它會被傳遞給第二個 catch 塊。如此,直到異常被捕獲或者通過所有的 catch 塊。
try...catch...catch... 捕獲異常時,大的異常(Exception類)放在下方,小的異常放在上方,否則,在異常捕獲時,小的異常将不能被捕獲,因為全在大的異常類中捕獲到。即如果多個 catch 塊中的異常出現繼承關系,父類異常 catch 塊放在最下面。
finally
finally 代碼塊出現在 catch 代碼塊最後,文法如下:
try{
// 程式代碼
}catch(異常類型1 異常的變量名1){
// 程式代碼
}catch(異常類型2 異常的變量名2){
// 程式代碼
}finally{
// 程式代碼
}
捕獲異常的最後一步是通過finally語句為異常處理提供一個統一的出口,使得在控制流轉到程式的其它部分以前,能夠對程式的狀态作統一的管理。
不論在try代碼塊中是否發生了異常事件,catch語句是否執行,catch語句是否有異常,catch語句中是否有return,finally塊中的語句都會被執行。
finally主要用于關閉資源。無論是否發生異常,資源都必須進行關閉。隻有在finally之前執行了System.exit(0); 進而退出jvm的這種情況下finally才不執行。
finally語句和catch語句是任選的。
import java.io.*;
public class ExcepTest{
public static void main(String args[]){
int a[] = new int[2];
try{
// 可能産生異常的代碼,即需要被檢測的代碼
System.out.println("擷取數組的第三個元素:" + a[3]);
}catch(ArrayIndexOutOfBoundsException e){ // e用于接收try檢測到的異常對象
System.out.println("抛出的異常:" + e);
}finally{ // finally 關鍵字用來建立在 try 代碼塊後面執行的代碼塊。
a[0] = 6;
System.out.println("數組第一個元素的值:" +a[0]);
}
}
}
執行結果:
抛出的異常:java.lang.ArrayIndexOutOfBoundsException: 3
數組第一個元素的值:6
finally 代碼塊和 finalize()方法有什麼差別?
無論是否抛出異常, finally 代碼塊都會執行,它主要是用來釋放應用占用的資源。 finalize()方法是 Object 類的一個 protected 方法,它是在對象被垃圾回收之前由 Java 虛拟機來調用的。
使用異常捕獲時注意下面幾個事項:
1)catch 不能獨立于 try 存在。
2)在 try/catch 後面添加 finally 塊并非強制性要求的。
3)try 代碼後不能既沒 catch 塊也沒 finally 塊。
4)try, catch, finally 塊之間不能添加任何代碼。
不捕獲異常時的情況
前面使用的異常都是RuntimeException類或是它的子類,這些類的異常的特點是:即使沒有使用try和catch捕獲,Java自己也能捕獲,并且編譯通過 ( 但運作時會發生異常使得程式運作終止 )。
如果抛出的異常是IOException等類型的非運作時異常,則必須捕獲,否則編譯錯誤。也就是說,我們必須處理編譯時異常,将異常進行捕捉,轉化為運作時異常。
注意:
1)異常處理完成以後,Exception對象會在下一個垃圾回收過程中被回收掉。
2)Java 語言定義了一些異常類在 java.lang 标準包中。标準運作時異常類的子類是最常見的異常類。由于 java.lang 包是預設加載到所有的 Java 程式的,是以大部分從運作時異常類繼承而來的異常都可以直接使用。