1. 程式人生 > 實用技巧 >異常機制

異常機制

一、什麼是異常

  • 實際工作中,遇到的情況不可能是非常完美的
    • 比如:你寫的某個模組。使用者不一定符合你的要求,你的程式要開啟某個檔案,這個檔案可能不存在或檔案格式不對,你要讀取資料庫的資料,資料可能是空的。我們程式在跑著,記憶體或硬碟可能就滿了
  • 然間程式在執行過程中,非常可能遇到剛剛提到的這些異 常問題,我們叫異常,英文名是Exception
    • 意思是例外。這些例外情況,或者叫異常,怎麼讓我們寫的程式做出合理的處理,而不至於程式崩潰
  • 異常指程式執行中出現的不期而至的各種情況,如:檔案找不到、網路連線失敗、非法引數等
  • 異常發生在程式執行期間,它影響了正常的程式執行流程

1. 例子

  • error
    • a呼叫b,b呼叫a
package com.exception;

public class Demo01 {

    public static void main(String[] args) {

        //匿名內部類
        new Demo01().a();

    }

    public void a(){
        b();
    }

    public void b(){
        a();
    }
}
/*
            Exception in thread "main" java.lang.StackOverflowError
            at com.exception.Demo01.b(Demo01.java:16)
            at com.exception.Demo01.a(Demo01.java:12)
            at com.exception.Demo01.b(Demo01.java:16)
            at com.exception.Demo01.a(Demo01.java:12)
 */
  • 異常
    • 0不能為被除數
public class Demo02 {
    public static void main(String[] args) {
        System.out.println(11/0);
    }
}
/*

    Exception in thread "main" java.lang.ArithmeticException: / by zero
        at com.exception.Demo02.main(Demo02.java:5)

 */

2. 異常的簡單分類

  • 要理解Java是如何工作的,需要掌握三種類型的異常

    • 執行時異常——是由個人問題造成的

      • 是可能被避免的異常,執行時異常可以在編譯時被忽略(與檢查性異常相反)
    • 非執行時異常

      • 檢查性異常

        • 最具代表性的異常,是使用者錯誤或問題引起的異常
          • 如果要開啟一個不存在的檔案,一個異常就發生了,這些異常在編譯時不能被簡單地忽略
      • 錯誤ERROR

        • 錯誤不是異常,是脫離程式設計師控制的問題,錯誤在程式碼中通常被忽略
          • 例如,當棧溢位時,一個錯誤就發生了,他們在編譯也檢查不到

二、異常體系結構

  • Java把異常當作物件來處理,並定義了一個基類java.lang.Throwable作為所有異常的超類
  • 在Java API中已經定義了許多異常類,這些異常分為兩大類,錯誤Error和異常Exception

1. 錯誤Error

  • Error類物件由Java虛擬機器生成並丟擲,大多數錯誤程式碼編寫這所執行的操作無關
  • Java虛擬機器執行錯誤,當JVM不在有繼續執行操作所需的記憶體資源時,將出現OutofMemoryError。這些異常發生錯誤是,Java虛擬機器(JVM)一般會選擇執行緒終止
  • 還有發生在虛擬機器試圖執行應用時,如類定義錯誤(NoClassDefFoundError)、連結錯誤(LinkageError)這些錯誤是不可查的,因為它們在應用程式的控制和處理能力之外,而且絕大多數是程式執行時不允許出現的情況

2. 異常Exception

  • 在Exception分支中有一個重要的子類RuntimeException(執行時異常)
    • ArrayIndexOutBoundsException(陣列下標越界)
    • NullPointerException(空指標異常)
    • ArithmeticException(算數異常)
    • MissingResourceException(丟失資源)
    • ClassNotFoundException(找不到類)等異常,
  • 這些異常時不檢查異常,程式中可以選擇捕獲處理,也可以不處理
  • 這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這些異常的發生

3. Error和Exception的區別

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

三、Java異常處理機制

  • 丟擲異常
  • 捕獲異常
  • 異常處理五個關鍵字
    • try、catch、finally、throw、throws

1. try、catch、final

  • 異常型別——ArithmeticException
public class Test {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;

        System.out.println(a/b);

    }
}
/*
    Exception in thread "main" java.lang.ArithmeticException: / by zero
        at com.exception.Test.main(Test.java:8)
*/
  • try監控區域
  • catch 捕獲異常——想要捕獲的異常型別
  • final處理善後——可以沒有,但是寫了一定會執行完final再報錯
    • 假設IO流,資源scanner等的關閉
public class Test {
    public static void main(String[] args) {
        int a = 1;
        int b = 0;

        try {
            System.out.println(a/b);
        }catch (ArithmeticException e){//捕獲ArithmeticException
            System.out.println("程式出現異常,變數b不能為0");
        }finally {
            System.out.println("finally");
        }

    }

}
/*
    程式出現異常,變數b不能為0
    finally
*/

1.1 final一定會執行

public class Test {
    public static void main(String[] args) {

        try {
              //等價於new Test().a();
              Test test = new Test();
              test.a();
        }catch (ArithmeticException e){
            System.out.println("程式出現異常,變數b不能為0");
        }finally {
            System.out.println("finally");
        }

    }

    public void a(){
        b();
    }

    public void b(){
        a();
    }
}
/*
        finally
        Exception in thread "main" java.lang.StackOverflowError
            at Test.b(Test.java:23)
            at Test.a(Test.java:19)
            at Test.b(Test.java:23)
            at Test.a(Test.java:19)
 */
  • 將捕獲類ArithmeticException改為捕獲類Throwable後
    • 類Throwable包含類StackOverflowError
輸出結果
/*
        程式出現異常,變數b不能為0
        finally
*/

1.2 可以寫多個catch

  • 要捕獲多個異常,從小到大
public class Test {
    public static void main(String[] args) {

        int a =1;
        int b =0;

        try {
            System.out.println(a/b);
        }catch (Error e){
            System.out.println("Error");
        }catch (Exception e){
            System.out.println("Exception");
        }catch (Throwable e){//最大的異常寫在最下面,否則會報錯,Throwable覆蓋Error和Exception
            System.out.println("Throwable");
        }finally {
            System.out.println("finally");
        }

    }

    public void a(){
        b();
    }

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

1.3 快捷鍵Ctrl+Alt+L

  • 自動生成try,catch

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

        try {
            System.out.println(a/b);
        } catch (Exception e) {
            System.exit(1);//可以自定義一些語句
            e.printStackTrace();//預設,列印錯誤的棧資訊
        } finally {
        }
    }

2. throw和throws

2.1 throw

  • throw是語句丟擲一個異常
  • 一般用在方法中
public class Test {
    public static void main(String[] args) {

        new Test().test(1,0);

    }

    public void test(int a,int b) {
        System.out.println(a/b);
    }

}
/*
        Exception in thread "main" java.lang.ArithmeticException: / by zero
            at com.exception.Test.test(Test.java:14)
            at com.exception.Test.main(Test.java:6)
*/
  • 算數異常屬於執行時異常,系統會自動丟擲,不需要主動丟擲
  • 利用throw主動丟擲異常
package com.exception;

public class Test {
    public static void main(String[] args) {

        new Test().test(1,0);

    }

    public void test(int a,int b) {
        if (b==0) {
            throw new ArithmeticException();
        }
        System.out.println(a/b);
    }

}
/*
        Exception in thread "main" java.lang.ArithmeticException
            at com.exception.Test.test(Test.java:12)
            at com.exception.Test.main(Test.java:6)
*/

2.2 throws

  • throws是方法可能丟擲異常的宣告
  • 一般用在方法中
public class Test {
    public static void main(String[] args) {

        try {
            new Test().test(1,0);
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void test(int a,int b) throws ArithmeticException{
        if (b==0) {
//            throw new ArithmeticException();
            System.out.println("throws");
        }
    }

}

2.3 throw和throws的區別

參考資料

四、自定義異常

  • 使用Java內建的異常類可以描述在程式設計時出現的大部分異常情況,除此之外,使用者還可以自定義異常,使用者自定義異常類,只需要繼承Exception類即可
  • 在程式中使用自定義異常,大體可以分為以下幾個步驟
      1. 建立自定義異常類
      2. 在方法中通過throw關鍵詞丟擲異常物件
      3. 如果在當前丟擲異常的方法中處理異常,可以使用try-catch語句捕獲並處理;否則在方法的宣告處通過throws關鍵詞知名要跑出給方法呼叫者的異常,繼續進行下一步操作
      4. 在出現異常方法的呼叫者中捕獲並處理異常

1. 例子

  • 自定義異常類MyException
//繼承了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 +
                '}';
    }
}
  • 測試類Test
public class Test {

    //可能會存在異常的方法
    public void test(int a) throws MyException{//選擇法2丟擲異常
        System.out.println("傳遞的引數為"+a);

        if (a>10) {
            throw new MyException(a);//丟擲了一個異常
            //解決方法1,把這個異常捕獲
            //解決方法2. 把異常丟擲,拋到呼叫這個方法的人中捕獲它
        }

        System.out.println("OK");
    }

}
  • main方法
import com.exception.demo02.MyException;
import com.exception.demo02.Test;

public class Application {
    public static void main(String[] args) {

        Test test = new Test();

        try {
            test.test(11);
        } catch (MyException e) {
            //此處需要增加一些處理異常的程式碼
            System.out.println("MyException=>"+e);
        }

    }
}
/*
**********************************
test.test(10);
**********************************
        傳遞的引數為10
        OK
**********************************
test.test(11);
**********************************
        傳遞的引數為11
        MyException=>MyException{detail=11}
*/
  • 先自定義了一個異常類MyException
    • 構造器初始化detail的值為 傳進來的值a
    • 然後列印輸出一句話MyException=>MyException{detail=傳進來的值a}
  • 在main中例項化測試類Test後
    • try監控呼叫類Test物件test中的test方法
      • 被呼叫test方法
        • 當a的值<10時,則不會丟擲異常MyException
          • 正常輸出內容
        • 當a的值>10時,會丟擲一個異常MyException
          • catch捕獲異常MyException,並處理解決異常
            • 列印輸出一句MyException=>e
              • e代表呼叫執行MyException這給類

五、總結

  • 處理執行時異常,採用邏輯去合理規避同時輔助try-catch處理
    • 可避免程式卡住進行不下去
  • 在多重catch塊後面,可以加上一個catch(Exception)來處理可能會被遺漏的異常
  • 對於不確定的程式碼,也可以加上try-catch,處理潛在的異常
  • 儘量去處理異常,不要只是簡單地呼叫printStckTrace()去列印輸出
    • 增加一些處理異常的程式碼
  • 具體如何處理異常,要根據不同的業務需求和異常型別去決定
  • 儘量新增finally語句塊去釋放佔用資源
    • Scanner