Java 異常處理

Java 異常處理

異常是程式中的一些錯誤,但並不是所有的錯誤都是異常,並且錯誤有時候是可以避免的。

比如說,你的程式碼少了一個分號,那麼執行出來結果是提示是錯誤 java.lang.Error;如果你用System.out.println(11/0),那麼你是因為你用0做了除數,會丟擲 java.lang.ArithmeticException 的異常。

異常發生的原因有很多,通常包含以下幾大類:

  • 使用者輸入了非法資料。
  • 要開啟的檔案不存在。
  • 網路通訊時連線中斷,或者JVM記憶體溢位。

這些異常有的是因為使用者錯誤引起,有的是程式錯誤引起的,還有其它一些是因為物理錯誤引起的。-

要理解Java異常處理是如何工作的,你需要掌握以下三種類型的異常:

  • 檢查性異常:最具代表的檢查性異常是使用者錯誤或問題引起的異常,這是程式設計師無法預見的。例如要開啟一個不存在檔案時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
  • 執行時異常: 執行時異常是可能被程式設計師避免的異常。與檢查性異常相反,執行時異常可以在編譯時被忽略。
  • 錯誤: 錯誤不是異常,而是脫離程式設計師控制的問題。錯誤在程式碼中通常被忽略。例如,當棧溢位時,一個錯誤就發生了,它們在編譯也檢查不到的。

Exception 類的層次

所有的異常類是從 java.lang.Exception 類繼承的子類。

Exception 類是 Throwable 類的子類。除了Exception類外,Throwable還有一個子類Error 。

Java 程式通常不捕獲錯誤。錯誤一般發生在嚴重故障時,它們在Java程式處理的範疇之外。

Error 用來指示執行時環境發生的錯誤。

例如,JVM 記憶體溢位。一般地,程式不會從錯誤中恢復。

異常類有兩個主要的子類:IOException 類和 RuntimeException 類。

在 Java 內建類中(接下來會說明),有大部分常用檢查性和非檢查性異常。


Java 內建異常類

Java 語言定義了一些異常類在 java.lang 標準包中。

標準執行時異常類的子類是最常見的異常類。由於 java.lang 包是預設載入到所有的 Java 程式的,所以大部分從執行時異常類繼承而來的異常都可以直接使用。

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 請求的方法不存在

異常方法

下面的列表是 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 物件棧層次,新增到棧層次任何先前資訊中。

捕獲異常

使用 try 和 catch 關鍵字可以捕獲異常。try/catch 程式碼塊放在異常可能發生的地方。

try/catch程式碼塊中的程式碼稱為保護程式碼,使用 try/catch 的語法如下:

try
{
   // 程式程式碼
}catch(ExceptionName e1)
{
   //Catch 塊
}

Catch 語句包含要捕獲異常型別的宣告。當保護程式碼塊中發生一個異常時,try 後面的 catch 塊就會被檢查。

如果發生的異常包含在 catch 塊中,異常會被傳遞到該 catch 塊,這和傳遞一個引數到方法是一樣。

例項

下面的例子中宣告有兩個元素的一個數組,當代碼試圖訪問陣列的第三個元素的時候就會丟擲一個異常。

ExcepTest.java 檔案程式碼:

// 檔名 : 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"); } }

以上程式碼編譯執行輸出結果如下:

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

多重捕獲塊

一個 try 程式碼塊後面跟隨多個 catch 程式碼塊的情況就叫多重捕獲。

多重捕獲塊的語法如下所示:

try{ // 程式程式碼 }catch(異常型別1 異常的變數名1){ // 程式程式碼 }catch(異常型別2 異常的變數名2){ // 程式程式碼 }catch(異常型別3 異常的變數名3){ // 程式程式碼 }

上面的程式碼段包含了 3 個 catch塊。

可以在 try 語句後面新增任意數量的 catch 塊。

如果保護程式碼中發生異常,異常被拋給第一個 catch 塊。

如果丟擲異常的資料型別與 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; }

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 }

finally關鍵字

finally 關鍵字用來建立在 try 程式碼塊後面執行的程式碼塊。

無論是否發生異常,finally 程式碼塊中的程式碼總會被執行。

在 finally 程式碼塊中,可以執行清理型別等收尾善後性質的語句。

finally 程式碼塊出現在 catch 程式碼塊最後,語法如下:

try{ // 程式程式碼 }catch(異常型別1 異常的變數名1){ // 程式程式碼 }catch(異常型別2 異常的變數名2){ // 程式程式碼 }finally{ // 程式程式碼 }

例項

ExcepTest.java 檔案程式碼:

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"); } } }

以上例項編譯執行結果如下:

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 塊之間不能新增任何程式碼。

宣告自定義異常

在 Java 中你可以自定義異常。編寫自己的異常類時需要記住下面的幾點。

  • 所有異常都必須是 Throwable 的子類。
  • 如果希望寫一個檢查性異常類,則需要繼承 Exception 類。
  • 如果你想寫一個執行時異常類,那麼需要繼承 RuntimeException 類。

可以像下面這樣定義自己的異常類:

class MyException extends Exception{ }

只繼承Exception 類來建立的異常類是檢查性異常類。

下面的 InsufficientFundsException 類是使用者定義的異常類,它繼承自 Exception。

一個異常類和其它任何類一樣,包含有變數和方法。

例項

以下例項是一個銀行賬戶的模擬,通過銀行卡的號碼完成識別,可以進行存錢和取錢的操作。

InsufficientFundsException.java 檔案程式碼:

// 檔名InsufficientFundsException.java import java.io.*; //自定義異常類,繼承Exception類 public class InsufficientFundsException extends Exception { //此處的amount用來儲存當出現異常(取出錢多於餘額時)所缺乏的錢 private double amount; public InsufficientFundsException(double amount) { this.amount = amount; } public double getAmount() { return amount; } }

為了展示如何使用我們自定義的異常類,

在下面的 CheckingAccount 類中包含一個 withdraw() 方法丟擲一個 InsufficientFundsException 異常。

CheckingAccount.java 檔案程式碼:

// 檔名稱 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 <= balance) { balance -= amount; } else { double needs = amount - balance; throw new InsufficientFundsException(needs); } } //方法:返回餘額 public double getBalance() { return balance; } //方法:返回卡號 public int getNumber() { return number; } }

下面的 BankDemo 程式示範瞭如何呼叫 CheckingAccount 類的 deposit() 和 withdraw() 方法。

BankDemo.java 檔案程式碼:

//檔名稱 BankDemo.java public class BankDemo { public static void main(String [] args) { CheckingAccount c = new CheckingAccount(101); System.out.println("Depositing $500..."); c.deposit(500.00); try { System.out.println("\nWithdrawing $100..."); c.withdraw(100.00); System.out.println("\nWithdrawing $600..."); c.withdraw(600.00); }catch(InsufficientFundsException e) { System.out.println("Sorry, but you are short $" + e.getAmount()); e.printStackTrace(); } } }

編譯上面三個檔案,並執行程式 BankDemo,得到結果如下所示:

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
        at CheckingAccount.withdraw(CheckingAccount.java:25)
        at BankDemo.main(BankDemo.java:13)

通用異常

在Java中定義了兩種型別的異常和錯誤。

  • JVM(Java虛擬機器) 異常:由 JVM 丟擲的異常或錯誤。例如:NullPointerException 類,ArrayIndexOutOfBoundsException 類,ClassCastException 類。
  • 程式級異常:由程式或者API程式丟擲的異常。例如 IllegalArgumentException 類,IllegalStateException 類。