Java異常處理
1、Java異常概念
異常處理機制能讓程序在異常發生時,按照代碼的預先設定的異常處理邏輯,針對性地處理異常,讓程序盡最大可能恢復正常並繼續執行,且保持代碼的清晰。
要理解Java異常處理是如何工作的,你需要掌握以下三種類型的異常:
- 檢查性異常:最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程序員無法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
- 運行時異常: 運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常可以在編譯時被忽略。
- 錯誤: 錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中通常被忽略。例如,當棧溢出時,一個錯誤就發生了,它們在編譯也檢查不到的。
2、Java異常體系結構
Java標準庫內建了一些通用的異常,這些類以Throwable爲頂層父類。
Throwable又派生出Error類和Exception類。
錯誤:Error類以及他的子類的實例,代表了JVM本身的錯誤。錯誤不能被程序員通過代碼處理,Error很少出現。因此,程序員應該關注Exception爲父類的分支下的各種異常類。
異常:Exception以及他的子類,代表程序運行時發送的各種不期望發生的事件。可以被Java異常處理機制使用,是異常處理的核心。
總體上我們根據Javac對異常的處理要求,將異常類分爲2類。
非檢查異常(unckecked exception):
Error 和 RuntimeException 以及他們的子類。javac在編譯時,不會提示和發現這樣的異常,不要求在程序處理這些異常。所以如果願意,我們可以編寫代碼處理(使用try…catch…finally)這樣的異常,也可以不處理。對於這些異常,我們應該修正代碼,而不是去通過異常處理器處理 。這樣的異常發生的原因多半是代碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制類型轉換錯誤ClassCastException,數組索引越界ArrayIndexOutOfBoundsException,使用了空對象NullPointerException等等。
檢查異常(checked exception):
除了Error 和 RuntimeException的其它異常。javac強制要求程序員爲這樣的異常做預備處理工作(使用try…catch…finally或者throws)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws子句聲明拋出它,否則編譯不會通過。這樣的異常一般是由程序的運行環境導致的。因爲程序可能被運行在各種未知的環境下,而程序員無法干預用戶如何使用他編寫的程序,於是程序員就應該爲這樣的異常時刻準備着。如SQLException , IOException,ClassNotFoundException 等。
3、Java內置異常類
Java 語言定義了一些異常類在 java.lang 標準包中。
Java 根據各個類庫也定義了一些其他的異常,下面的表中列出了 Java 的非檢查性異常。
異常 | 描述 |
---|---|
ArithmeticException | 當出現異常的運算條件時,拋出此異常。例如,一個整數"除以零"時,拋出此類的一個實例。 |
ArrayIndexOutOfBoundsException | 用非法索引訪問數組時拋出的異常。如果索引爲負或大於等於數組大小,則該索引爲非法索引。 |
ArrayStoreException | 試圖將錯誤類型的對象存儲到一個對象數組時拋出的異常。 |
ClassCastException | 當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。 |
IllegalArgumentException | 拋出的異常表明向方法傳遞了一個不合法或不正確的參數。 |
IllegalMonitorStateException | 拋出的異常表明某一線程已經試圖等待對象的監視器,或者試圖通知其他正在等待對象的監視器而本身沒有指定監視器的線程。 |
IllegalStateException | 在非法或不適當的時間調用方法時產生的信號。換句話說,即 Java 環境或 Java 應用程序沒有處於請求操作所要求的適當狀態下。 |
IllegalThreadStateException | 線程沒有處於請求操作所要求的適當狀態時拋出的異常。 |
IndexOutOfBoundsException | 指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。 |
NegativeArraySizeException | 如果應用程序試圖創建大小爲負的數組,則拋出該異常。 |
NullPointerException | 當應用程序試圖在需要對象的地方使用 null 時,拋出該異常 |
NumberFormatException | 當應用程序試圖將字符串轉換成一種數值類型,但該字符串不能轉換爲適當格式時,拋出該異常。 |
SecurityException | 由安全管理器拋出的異常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此異常由 String 方法拋出,指示索引或者爲負,或者超出字符串的大小。 |
UnsupportedOperationException | 當不支持請求的操作時,拋出該異常。 |
下面的表中列出了 Java 定義在 java.lang 包中的檢查性異常類。
異常 | 描述 |
---|---|
ClassNotFoundException | 應用程序試圖加載類時,找不到相應的類,拋出該異常。 |
CloneNotSupportedException | 當調用 Object 類中的 clone 方法克隆對象,但該對象的類無法實現 Cloneable 接口時,拋出該異常。 |
IllegalAccessException | 拒絕訪問一個類的時候,拋出該異常。 |
InstantiationException | 當試圖使用 Class 類中的 newInstance 方法創建一個類的實例,而指定的類對象因爲是一個接口或是一個抽象類而無法實例化時,拋出該異常。 |
InterruptedException | 一個線程被另一個線程中斷,拋出該異常。 |
NoSuchFieldException | 請求的變量不存在 |
NoSuchMethodException | 請求的方法不存在 |
4、Throwable類異常方法
異常 | 描述 |
---|---|
1 | public String getMessage() 返回關於發生的異常的詳細信息。這個消息在Throwable 類的構造函數中初始化了。 |
2 | public Throwable getCause()返回一個Throwable 對象代表異常原因。 |
3 | public String toString()使用getMessage()的結果返回類的串級名字。 |
4 | public void printStackTrace()打印toString()結果和棧層次到System.err,即錯誤輸出流。 |
5 | public StackTraceElement [] getStackTrace()返回一個包含堆棧層次的數組。下標爲0的元素代表棧頂,最後一個元素代表方法調用堆棧的棧底。 |
6 | public Throwable fillInStackTrace()用當前的調用棧層次填充Throwable 對象棧層次,添加到棧層次任何先前信息中。 |
5、捕獲異常
5.1 try,catch
使用 try 和 catch 關鍵字可以捕獲異常。try/catch 代碼塊放在異常可能發生的地方。
try/catch代碼塊中的代碼稱爲保護代碼,使用 try/catch 的語法如下:
try
{
// 程序代碼
}catch(ExceptionName e1)
{
//Catch 塊
}
5.2 多重捕獲塊
一個 try 代碼塊後面跟隨多個 catch 代碼塊的情況就叫多重捕獲。
多重捕獲塊的語法如下所示:
try{
// 程序代碼
}catch(異常類型1 異常的變量名1){
// 程序代碼
}catch(異常類型2 異常的變量名2){
// 程序代碼
}catch(異常類型2 異常的變量名2){
// 程序代碼
}
上面的代碼段包含了 3 個 catch塊。
可以在 try 語句後面添加任意數量的 catch 塊。
如果保護代碼中發生異常,異常被拋給第一個 catch 塊。
如果拋出異常的數據類型與 ExceptionType1 匹配,它在這裏就會被捕獲。
如果不匹配,它會被傳遞給第二個 catch 塊。
如此,直到異常被捕獲或者通過所有的 catch 塊。
5.3 throws/throw 關鍵字:
如果一個方法沒有捕獲到一個檢查性異常,那麼該方法必須使用 throws 關鍵字來聲明。throws 關鍵字放在方法簽名的尾部。
也可以使用 throw 關鍵字拋出一個異常,無論它是新實例化的還是剛捕獲到的。
下面方法的聲明拋出一個 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
}
5.4 finally關鍵字
finally 關鍵字用來創建在 try 代碼塊後面執行的代碼塊。
無論是否發生異常,finally 代碼塊中的代碼總會被執行。
在 finally 代碼塊中,可以運行清理類型等收尾善後性質的語句。
finally 代碼塊出現在 catch 代碼塊最後,語法如下:
try{
// 程序代碼
}catch(異常類型1 異常的變量名1){
// 程序代碼
}catch(異常類型2 異常的變量名2){
// 程序代碼
}finally{
// 程序代碼
}
注意:
finally塊不管異常是否發生,只要對應的try執行了,則它一定也執行。只有一種方法讓finally塊不執行:System.exit()。因此finally塊通常用來做資源釋放操作:關閉文件,關閉數據庫連接等等。
良好的編程習慣是:在try塊中打開資源,在finally塊中清理釋放這些資源。
需要注意的地方:
1、finally塊沒有處理異常的能力。處理異常的只能是catch塊。
2、在同一try…catch…finally塊中 ,如果try中拋出異常,且有匹配的catch塊,則先執行catch塊,再執行finally塊。如果沒有catch塊匹配,則先執行finally,然後去外面的調用者中尋找合適的catch塊。
3、在同一try…catch…finally塊中 ,try發生異常,且匹配的catch塊中處理異常時也拋出異常,那麼後面的finally也會執行:首先執行finally塊,然後去外圍調用者中尋找合適的catch塊。
6、自定義異常
如果要自定義異常類,則擴展Exception類即可,因此這樣的自定義異常都屬於檢查異常(checked exception)。如果要自定義非檢查異常,則擴展自RuntimeException。
異常的注意事項
1、當子類重寫父類的帶有 throws聲明的函數時,其throws聲明的異常必須在父類異常的可控範圍內——用於處理父類的throws方法的異常處理器,必須也適用於子類的這個帶throws方法 。這是爲了支持多態。
例如,父類方法throws 的是2個異常,子類就不能throws 3個及以上的異常。父類throws IOException,子類就必須throws IOException或者IOException的子類。
7、finally塊和return
首先一個不容易理解的事實:在 try塊中即便有return,break,continue等改變執行流的語句,finally也會執行。
public static void main(String[] args)
{
int re = bar();
System.out.println(re);
}
private static int bar()
{
try{
return 5;
} finally{
System.out.println("finally");
}
}
/*輸出:
finally
*/
也就是說:try…catch…finally中的return 只要能執行,就都執行了,他們共同向同一個內存地址(假設地址是0×80)寫入返回值,後執行的將覆蓋先執行的數據,而真正被調用者取的返回值就是最後一次寫入的。
轉載至:
Java 異常處理
Java 中的異常和處理詳解