Java異常機制

Java異常機制

異常

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

異常的分類:

  • 檢查性異常:最具代表的檢查性異常是用戶錯誤或問題引起的異常,這是程序員無法預見的。例如要打開一個不存在文件時,一個異常就發生了,這些異常在編譯時不能被簡單地忽略。
  • 運行時異常: 運行時異常是可能被程序員避免的異常。與檢查性異常相反,運行時異常可以在編譯時被忽略。
  • 錯誤(Error): 錯誤不是異常,而是脫離程序員控制的問題。錯誤在代碼中通常被忽略。例如,當棧溢出時,一個錯誤就發生了,在編譯時同樣檢查不到。

示例:

package com.wmwx.exception;

public class Demo01 {
    public static void main(String[] args) {
        new Demo01().a();           //拋出異常:StackOverflowError
        System.out.println(11/0);   //拋出異常:ArithmeticException
        System.out.println()        //拋出異常:Error
    }

    public void a(){
        b();
    }

    public void b(){
        a();
    }
}

異常體系結構

Java把異常當做對象來處理,並定義了一個基類java.lang.Throwable作為所有異常的超類。這些異常分為兩大類,即異常(Exception)和錯誤(Error)。其中,Exception類有兩個主要的子類,即 IOException 類和 RuntimeException 類。

Error

Error類對象由虛擬機生成並拋出,大多數錯誤與代碼編寫者所執行的操作無關。

常見的錯誤如Java虛擬機運行錯誤(VirtualMachineError),當JVM不再有繼續執行操作所需的內存資源時,將出現OutOfMemoryError。這些異常發生時,Java虛擬機一般會選擇將線程中止。

還有發生在虛擬機試圖執行應用時的錯誤,如類定義錯誤(NoClassDefFoundError)、鏈接錯誤(LinkageError)等,這些錯誤是不可查的。因為它們在應用程序的控制和處理程序能力之外,而且絕大多數是程序運行時不允許出現的狀況。

Exception

在Exception類的分支中有一個重要的子類 RuntimeException(運行時異常),其中包含以下異常:

  • ArrayIndexOutOfBoundsException(數組下標越界)

  • NullPointerException(空指針異常)

  • ArithmeticException(算術異常)

  • MissingResourceException(丟失資源)

  • ClassNotFoundException(找不到類)

這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯誤引起的,程序應該從邏輯角度儘可能避免這類異常的發生。

Error和Exception的區別:

  • Error 通常是災難性的致命的錯誤,是程序無法控制和處理的,當出現這些異常時,Java虛擬機(JVM)一般會選擇終止線程
  • Exception 通常情況下是可以被程序處理的,並且在程序中應該儘可能的去處理這些異常。

捕獲異常

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

try/catch代碼塊中的代碼稱為保護代碼,其基本語法如下:

try
{
   // 程序代碼
}catch(ExceptionName e1)
{
   // Catch 塊
}catch(ExceptionName e2)	//e2需比e1範圍更大
{
   // Catch 塊
}finally {
   // 終究會執行
}

示例:

package com.wmwx.exception;

public class Demo02 {
    public static void main(String[] args) {
        int a = 11;
        int b = 0;

        //快捷鍵 ctrl+alt+T
        try{                                //try 監控區域
            System.out.println(a/b);
        }catch (ArithmeticException e){     //catch 捕獲異常(參數是異常類型)
            System.out.println("程序出現異常,變量b不能為0!");
        }finally {                          //finally 善後工作
            System.out.println("finally終究會被執行。");
        }

        try{                                //監控區域
            new Demo02().a();
        }catch (StackOverflowError e){      //捕獲異常,多個類別需要從小到大
            System.out.println("StackOverflowError");   //會輸出
        }catch (Exception e){
            System.out.println("Exception");            //不會輸出
        }catch (Throwable t){
            System.out.println("Throwable");            //不會輸出
        }finally {                          //善後工作
            System.out.println("finally終究會被執行。");
        }
    }

    public void a(){
        b();
    }

    public void b(){
        a();
    }
}

拋出異常

如果一個方法沒有捕獲到一個檢查性異常,那麼該方法必須使用 throws 關鍵字來聲明。throws 關鍵字放在方法簽名的尾部。

也可以使用 throw 關鍵字拋出一個異常,無論它是新實例化的還是剛捕獲到的。

注意:兩種方式的拼寫不同,第一種末尾有 s,第二種沒有。

示例:

package com.wmwx.exception;

public class Demo03 {
    public static void main(String[] args) {
        int a = 11;
        int b = 0;

        try {
            new Demo03().devide(a, b);
        } catch (ArithmeticException e) {
            e.printStackTrace();
        } finally {
        }
    }

    //假設在方法中無法處理異常,可以用throws主動拋出異常
    public void devide(int a, int b) throws ArithmeticException{
        if (b==0){
            throw new ArithmeticException();    //使用throw主動拋出異常,一般用在方法中
        }
        System.out.println(a / b);
    }
}

自定義異常

使用Java內置的異常類可以描述在編程時出現的大部分異常精況。除此之外,用戶還可以自定義異常,只需繼承Exception類即可。

示例:

MyException類(自定義異常類):

package com.wmwx.exception;

//自定義的異常類
public class MyException extends Exception{
    //假設:當傳遞過來的數字大於10時拋出異常
    private int detail;
    //構造方法
    public MyException(int a) {
        this.detail = a;
    }
    //toString 打印異常信息
    @Override
    public String toString() {
        return "MyException{detail=" + detail + "}";
    }
}

Demo04類(測試類):

package com.wmwx.exception;

//自定義異常的測試類
public class Demo04 {
    //可能會存在異常的方法
    public void test(int a) throws MyException {
        System.out.println("傳遞的參數為:"+a);
        if (a>10){
            throw new MyException(a);   //拋出異常
        }
        System.out.println("沒有異常!");
    }

    public static void main(String[] args) {
        try {
            new Demo04().test(5);       //沒有異常
            new Demo04().test(15);      //有異常
        } catch (MyException e) {
            System.out.println("MyException => " + e);
        }
    }
}

經驗總結

  • 處理運行時異常時,採用邏輯去合理規避,同時輔助 try-catch 處理。
  • 在多重catch塊後面,可以加一個 catch (Exception) 來處理可能會被遺漏的異常。對於不確定的代碼,也可以加上try-catch,處理潛在的異常。
  • 盡量去處理異常,切忌只是簡單地調用 printStackTrace() 去打印輸出。具體如何處理異常,要根據不同的業務需求和異常類型去決定。
  • 盡量添加 finally 語句塊去釋放佔用的資源。
Tags: