1. 程式人生 > >Java 基礎-異常處理

Java 基礎-異常處理

在 Java 中聲明瞭很多異常類,每個異常類都表示一種執行錯誤。程式執行過程中發生一個可識別的執行錯誤時(可以找到與錯誤匹配的異常類,例如被除數為 0 時會觸發 java.lang.ArithmeticException),系統會丟擲對應異常類的物件。

參考:Java 異常處理

Java 異常處理機制的優點

  • 分離錯誤處理程式碼,使業務程式碼更專注
  • 按照型別對錯誤分組
  • 可以捕獲處理無法預測的錯誤
  • 異常類的物件包含了異常的充分資訊
  • 可以按照呼叫棧傳播錯誤,直到有處理錯誤的程式碼

錯誤分類

根據錯誤的嚴重程度不同,可以分為兩類:

  • 錯誤:致命的,無法處理的。最上層父類是 Error 類
  • 異常:非致命,可以捕獲並處理。最上層父類是 Exception 類

Throwable 是 Error 和 Exception 的父類。

Java 中有3種方式生成異常物件:

  • 由 Java 虛擬機器生成
  • 由 Java 類庫中的某些類生成
  • 在自己寫的程式中生成和丟擲異常物件

必須通過 throw 語句拋異常物件,異常物件必須是 Throwable 或其子類的例項。

throw new XXException();

XXException e = new XXException();
throw e;

Throwable 類的主要方法

方法 描述
public String getMessage() 獲取異常的詳細資訊
public Throwable getCause() 返回 Throwable 物件,代表異常的原因
public void printStackTrace() 列印toString()結果和棧層次到 System.err 錯誤輸出流。
public StackTraceElement [] getStackTrace() 返回一個包含堆疊層次的陣列。下標為0的元素代表棧頂,最後一個元素代表方法呼叫堆疊的棧底。

異常的分類

根據是否必須捕獲異常,可以將異常分為兩類:

非檢查型異常(也叫執行時異常)

非檢查型異常繼承自 RuntimeException,不需要在程式中進行捕獲,編譯器也不會檢查。之所以可以不捕獲,有兩個原因:

  • 引發這類異常的操作經常出現(例如使用物件時如果物件為 null 則拋異常)
  • 要拋的異常可以用其他方式解決(例如被除數為0會丟擲異常,但可以提前判斷被除數來防止這種異常)
public class Excep {
    public static void main(String[] args) {
        String[] arr = {"hello", "world"};
        int i = 0;
        while(i < 10) {
            System.out.println(arr[i++]);
        }
    }
}

上面這段程式碼並沒有捕獲異常,編譯通過,執行時會因為陣列下標越界而報錯:

[[email protected]_139_38_centos java]# java Excep
hello
world
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
	at Excep.main(Excep.java:6)

檢查型異常

對於檢查型異常,呼叫者必須捕獲並處理(try{...}catch(...){...})或也宣告丟擲(`throws XXException``)。如果所有方法都沒有處理這種異常,最終異常會交給 Java 虛擬機器來處理。編譯器會檢查這種異常。

public class T {
    public static void main(String[] args) {
        He h = new He();
        h.fun();
    }
}
class He {
    public void fun() throws Exception {
        throw new RuntimeException();
    }
}

上面這段程式碼中,類 He 中的 fun 方法會丟擲異常,當時呼叫者並沒有捕獲也沒有繼續丟擲,所以執行時會報錯:

T.java:4: unreported exception java.lang.Exception; must be caught or declared to be thrown
        h.fun();
             ^
1 error

如果異常一路都在拋,沒有方法處理,最後到了 main 方法還在拋異常public static void main(String[] args) throws Exception{...},那最後 Java 虛擬機器會接收到這個異常,程式會停止執行並報錯,例如:

public class T {
    public static void main(String[] args) throws Exception{
        He h = new He();
        h.fun();
        System.out.println("end of program");
    }
}
class He {
    public void fun() throws Exception {
        throw new RuntimeException();
    }
}
[[email protected]_139_38_centos java]# java T
Exception in thread "main" java.lang.RuntimeException
	at He.fun(T.java:10)
	at T.main(T.java:4)

捕獲並處理異常

Java 中用 try{...}catch(...){...}finally{...} 語句捕獲異常。其中,try 程式碼段中是可能丟擲異常的程式碼,catch 程式碼段在匹配異常時執行,finally 程式碼段則是無論是否發生異常都會執行的程式碼段。語法為:

try {
   // 程式程式碼
} catch(ExceptionName1 e1) {
   //Catch 塊
} catch(ExceptionName2 e2) {
   //可以有多個 Catch 塊
} finally {
	//finally 塊
}

例如,對於陣列越界異常的捕獲處理:

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);
      }finally{
          System.out.println("finally run");
      }
      System.out.println("Out of the block");
   }
}

結果為:

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

throws/throw 關鍵字

對於檢查性異常,如果方法不想捕獲,那麼該方法必須使用 throws 關鍵字來丟擲這個異常(如果有多個可能的異常,需要用逗號分隔),交給呼叫者來處理。throws 關鍵字放在方法簽名的尾部。

public void fun() throws RemoteException, InsufficientFundsException
{
    // Method implementation
}

throw 關鍵字用於丟擲異常給方法的呼叫者,注意需要在方法簽名尾部用 throws 丟擲這個異常。

public void fun() throws RemoteException
{
	throw new RemoteException();
}

自定義異常

Java 中的異常類跟普通的類一樣,有屬性和方法。根據自定義的異常是否需要檢查,可以分為兩類:

  • 檢查型異常類,需要繼承 Exception 類。
  • 執行時異常類,需要繼承 RuntimeException 類。

語法示例:

class MyException extends Exception{
}

程式碼示例:

public class T {
    public static void main(String[] args) {
        try {
            throw new MyException(666);
        } catch (MyException e) {
            System.out.println(e);
        }
    }
}
class MyException extends Exception {
    private int amount;
    public MyException(int amount) {
        this.amount = amount;
    }
    public String getMessage() {
        return "amount not enough" + this.amount;
    }
}

執行結果:

MyException: amount not enough666