天天看點

張小飛的Java之路——第十五章——異常

作者:小艾程式設計

寫在前面:

視訊是什麼東西,有看文檔精彩嗎?

視訊是什麼東西,有看文檔速度快嗎?

視訊是什麼東西,有看文檔效率高嗎?

1. 示範

諸小亮:異常,就是不正常,是在程式運作時出現的各種錯誤,比如:

public class Demo{
    public static void main(String[] args){
        int num = 2 / 0;//0不能作為除數,是以會産生異常
        System.out.println("程式結束。。。。");
    }
}           

結果:

張小飛的Java之路——第十五章——異常

張小飛:之前也遇到過很多次了,可以具體解釋一下這輸出都是什麼意思嗎?

諸小亮:這是必須的,上圖中可以分為3個部分:

  • ArithmeticException:是異常類型,
  • / by zero:是異常資訊
  • (Demo.java:5) 是異常出現的位置

張小飛:明白了

諸小亮:另外,出現異常後程式會終止,是以 System.out.println("程式結束。。。。"); 不運作

張小飛:噢,你不說還真沒注意,我記下了

2. 介紹

諸小亮:在 Java 中,用 Throwable類 對程式運作時的不正常情況進行了描述

張小飛:不對啊,上面的異常不是 ArithmeticException嗎?

諸小亮:你說的沒錯,但是 Java 中所有異常都是 Throwable 的子類,具體分為兩大類:

  • Exception:可以通過修改代碼進行處理的不正常現象,比如:ArithmeticException
  • Error:一般是系統底層問題,無法聽過代碼處理,比如:記憶體溢出
張小飛的Java之路——第十五章——異常

我們主要學習的是如何處理 Exception 這類異常

3. 處理異常

張小飛:那麼,如何處理異常呢?

諸小亮:可以使用 try catch 去處理

1. try catch

諸小亮:通過 try catch 代碼塊,捕獲指定的異常并處理,比如:

public static void main(String[] args){
    try {
        int num = 2 / 0;
    }catch (Exception e){//catch 裡面捕獲指定的異常類型
        
        //TODO 這裡書寫一些代碼處理這種異常情況
        
        //e.getMessage():擷取異常資訊
        System.out.println("發現錯誤了。。。。" + e.getMessage());
    }
    System.out.println("程式結束。。。。");
}           

結果:

張小飛的Java之路——第十五章——異常

,程式可以正常結束

張小飛:這句代碼 ‘catch (Exception e)’,表示 Exception 的子類型,都可以捕獲到嗎?

諸小亮:是的,然後就會執行 catch 中的代碼

張小飛:您看我這樣寫行不行

張小飛的Java之路——第十五章——異常

諸小亮:當然沒問題,但是這樣的話,如果代碼中有其他異常,就不能catch到,比如:

public static void main(String[] args){
    try {
        int[] arr = new int[2];
        System.out.println(arr[2]);
        int num = 2 / 0;
    }catch (ArithmeticException e){
        System.out.println("發現算術異常。。。。" + e.getMessage());
    }
    System.out.println("程式結束。。。。");
}           

結果:

張小飛的Java之路——第十五章——異常

解釋:因為我們 catch 的是 ArithmeticException ,卻出現了ArrayIndexOutOfBoundsException異常,是以并沒有走 catch中的代碼

張小飛:原來是這樣,我明白了

2. throws

諸小亮:下面我們看看——throws關鍵字

張小飛:這個是做什麼用的?

1. 示範

諸小亮;如果不想用 try catch 處理異常,可以通過 throws 關鍵字聲明異常,比如:

//throws 寫在方法上,用來聲明異常
public static int div(int a, int b) throws Exception{
    return a / b;
}           

張小飛:這是???

諸小亮:就是提醒調用者,這個方法可能發生異常

張小飛:您的意思是,在調用這個方法的時候需要用 try catch?

諸小亮:是的,誰調用誰處理,這時調用這個方法的地方就應該:

public static void main(String[] args){
    try{
        div(2,0);
    }catch (Exception e){
        //這裡處理異常
    }

}           

張小飛:如果調用者也不想處理呢?

諸小亮:那就繼續 throws,比如:

張小飛的Java之路——第十五章——異常

張小飛:這時候,如果異常,是誰處理?

諸小亮:就是 JVM 自己處理了

2. 多種異常

諸小亮:另外,throws 可以聲明多種異常,但是要逗号分隔,比如:

張小飛的Java之路——第十五章——異常

張小飛:這樣的話,catch 那裡怎麼辦呢?

諸小亮:如果聲明多個異常,catch 也應該有多個,保證每種異常都能被 cath 到并處理,比如:

public static void main(String[] args){
    try{
        div(2,0);
    }catch (ArrayIndexOutOfBoundsException e){

    }catch (ArithmeticException e){
        
    }
}
//throws 寫在方法上,用來聲明異常
public static int div(int a, int b) throws ArrayIndexOutOfBoundsException, ArithmeticException{
    return a / b;
}           

張小飛:原來如此

諸小亮:還可以優化

張小飛:哦?怎麼優化?

諸小亮:因為上面的異常都是 Excepetion 的子類,是以直接 catch 一個 Excetion 就行了,比如:

張小飛的Java之路——第十五章——異常

張小飛:明白了

3. 常用方法

諸小亮:接着我們說說 Exception 中的常用方法

1. getMessage()

諸小亮:getMessage() ,用來擷取異常資訊,已經示範過,就不在多說了

張小飛:好的

2. toString

諸小亮:toString(),把異常轉換為字元串輸出,你可以嘗試一下

張小飛:我試過了,您看看

張小飛的Java之路——第十五章——異常

結果:

張小飛的Java之路——第十五章——異常

3. printStackTrace

諸小亮:printStackTrace(),列印異常的堆棧資訊,這個功能比較有用

張小飛:是這樣嗎?比如:

張小飛的Java之路——第十五章——異常

結果:

張小飛的Java之路——第十五章——異常

諸小亮:沒錯,上圖中,展示出了具體報錯的位置

4. throw關鍵字

諸小亮:接下來,我們看看——throw關鍵字

張小飛:咦,這個不是剛才說過來嗎?

諸小亮:看清楚,剛才說的是 throws,現在是 throw,不一樣

張小飛:哦哦,這個 throw 又是什麼意思?

諸小亮:throw,用來抛出指定的異常,比如:

public static void main(String[] args){
    try {
        int num = div(2,0);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
public static int div(int a, int b) throws Exception{
    if(b == 0){
        //通過 throw 關鍵字,抛出指定的異常,
        //當程式運作到這裡後中斷,下面的 return a/b; 不再運作
        throw new ArithmeticException("國小沒畢業嗎?除數不能是0---"+b);
    }
    return a / b;
}           

結果:

張小飛的Java之路——第十五章——異常

張小飛:原來還可以這樣,不過這有什麼用呢?不用 throw 也會自動抛出異常啊?

張小飛的Java之路——第十五章——異常

諸小亮:這樣做,有兩個好處,第一,可以自定義異常資訊

張小飛:噢,确實,我們自定義的資訊更加清楚一些,第二呢?

諸小亮:第二,可以抛出其他異常,比如:

張小飛的Java之路——第十五章——異常

張小飛:明白了

諸小亮:另外,如果不知道應該抛出什麼異常,可以使用Exception,比如:

張小飛的Java之路——第十五章——異常

張小飛:我試了試,抛出這個是有問題的,您看看

張小飛的Java之路——第十五章——異常

諸小亮:因為,隻有 Throwable 類型的對象才能被 throw 抛出

張小飛:原來如此

諸小亮:來,我們總結一下 throw 跟 throws 的差別

  • 位置不同
    • throws:用在方法上,其後指定異常的種類可以有多個
    • throw:用在方法内,其後指定具體的異常對象
  • 功能不同
    • throws:聲明異常,告訴調用者可能會出現問題
    • throw:抛出異常,程式運作到此處時就中斷了

張小飛:明白了,不過,一般什麼時候用 throw 呢?

諸小亮:一般用來校驗參數,如果參數不對抛出異常

5. RuntimeException

諸小亮:下面我們說說——RuntimeException

張小飛:這個是什麼意思?

“RuntimeException,運作時異常,即:程式運作時抛出的異常,這是 Exception 下比較特殊的一個兒子”

“哦?怎麼個特殊法?”

“一般情況下,隻是在代碼中用 throw 抛出異常後,會編譯失敗,比如:”

張小飛的Java之路——第十五章——異常

“這時候需要在方法上使用 throws 聲明異常,比如:”

張小飛的Java之路——第十五章——異常

張小飛:不對吧,剛才我沒有用 throws ,也沒有編譯失敗啊

諸小亮:因為 IllegalArgumentException 是 RuntimeException 的子類

  • 使用 throw 抛出運作時異常,不需要在方法上聲明
張小飛的Java之路——第十五章——異常

張小飛:原來如此

張小飛:那,ArithmeticException ArrayIndexOutOfBoundsException,也是運作時異常?

諸小亮:是的

張小飛:為什麼這樣呢?使用 throws 後,調用者就需要用 try catch 處理,這樣不是更好嗎?

如果不聲明的話,調用者就可能忘記使用 try catch,這樣程式容易報錯啊

諸小亮:你說的沒錯,不過出現RuntimeException,一般都是調用者寫的代碼有問題

這樣做的目的:就是停止程式,讓調用者修改代碼

張小飛:原來是這樣

諸小亮:因為 RuntimeException,是以可以把 Exception 分為兩類——受檢 和 非受檢異常

張小飛:這是什麼意思?

諸小亮:給你解釋一下

  • 受檢異常:在編譯的時,要強制檢查的異常,這種異常需要去通過 try catch 來進行捕獲或者使用throws在方法上聲明,否則無法通過編譯的
  • 非受檢異常:在編譯的時,不需要去強制檢查的異常,這種異常可以不需要顯示去捕獲或者抛出

張小飛:那麼,RuntimeException 就是非受檢異常?

諸小亮:沒錯,在 Exception 下,RuntimeException就是非受檢異常,其他的都是受檢異常

張小飛:明白了

6. 自定義異常

張小飛:自定義異常——是自己定義一個類,來描述某種異常嗎?

諸小亮:是的

張小飛:Java已經提供 Exception,為什麼還要自定義異常呢?

諸小亮:Java 雖然用 Exception 描述程式運作時的不正常情況,但是現實中的異常多種多樣

其内部的異常類型(Exception的子類)可能不滿足實際需求,這時候就要自定義異常

1. 示範

諸小亮:比如,定義一個方法做除法運算,如果除數小于等于0,就抛出一個除數是負數的異常

目前 Java 中沒有定義除數是負數的異常,這時候就需要自定義

示例:

//自定義異常,注意:一般使用自定義異常都是起一些容易了解的名字,這樣閱讀性更好
class FuShuException extends Exception{

}

public class Demo{
    public static void main(String[] args) {
        
        try {
            //除數傳一個負數
        	int num = div(2,-1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static int div(int a, int b) throws FuShuException {
        if(b < 0){
            throw new FuShuException();
        }

        if(b == 0){
            throw new ArithmeticException("除數不能等于0。。。。。");
        }
        return a / b;
    }
}           

張小飛:沒有必要自定義吧,這樣子豈不是更好,您看看

張小飛的Java之路——第十五章——異常

諸小亮:你說的不錯,但是有時候需要針對不同的異常,做不同的處理,比如:

張小飛的Java之路——第十五章——異常

張小飛:明白了

2. 自定義異常資訊

張小飛:您定義的這個 FuShuException ,不能自定義異常資訊啊,IllegalArgumentException是可以的

他們不都是 Exception 的子類嗎?

諸小亮:這時因為,FuShuException少一個構造函數,比如:

FuShuException(String message){
    super(message);//調用 super,設定異常資訊
}           

張小飛:原來如此,我來試試

3. 繼承RuntimeException

諸小亮:另外,目前 FuShuException 繼承 Exception,必須在方法上用 throws 聲明。。。。

張小飛:這個簡單,直接繼承 RuntimeException 就行了,比如:

張小飛的Java之路——第十五章——異常
張小飛的Java之路——第十五章——異常

諸小亮:看來你已經充分了解:非受檢異常

7. finally

諸小亮:接着來看看——finally代碼塊

張小飛:這又是幹什麼用的?

1. 示範

諸小亮:finally 是配合 try catch 使用的一種特殊的代碼塊,比如:

public static void main(String[] args)  {
    try{
        int num = 2 /0 ;
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        System.out.println("finally中的代碼執行。。。。");
    }
}           

結果:

張小飛的Java之路——第十五章——異常

諸小亮:你有沒有什麼發現?

張小飛:finally 中的代碼,在 catch 處理異常後,正常執行

諸小亮:說得對,這就是它最重要的地方

2. finally中的代碼一定會執行

諸小亮:其實,finally代碼塊最重要的意義就是:它裡面的代碼一定會執行

張小飛:一定會執行?

“沒錯,即使在 catch 中使用 return 語句,finally也會執行,比如:”

public static void main(String[] args)  {
    try{
        int num = 2 /0 ;
    }catch (Exception e){
        e.printStackTrace();
        return;//catch中加上return語句,finally中的代碼依然執行
    }finally {
        System.out.println("finally中的代碼執行。。。。");
    }
}           

結果:

張小飛的Java之路——第十五章——異常

“有沒有什麼例外情況?”

“凡事都有例外,如果是退出 JVM 虛拟機,finally不執行,比如:”

張小飛的Java之路——第十五章——異常

結果:

張小飛的Java之路——第十五章——異常

3. return 和 finally

諸小亮:問你個問題,return後的語句和finally中的語句,哪個先執行?

張小飛:額....,稍等我驗證一下

public static void main(String[] args)  {
    show();
}

private static int show() {
    try{
        int num = 2 /0 ;
    }catch (Exception e){
        e.printStackTrace();
        //return 後面調用一個方法
        return test();
    }finally {
        System.out.println("finally中的代碼執行。。。。");
    }
    return 1;
}

private static int test() {
    System.out.println("傳回一個0.....");
    return 0;
}           

結果:

張小飛的Java之路——第十五章——異常

張小飛:這跟我預想的結果不太一樣,本來以為 finally 中的代碼先執行呢?

諸小亮:它的執行過程是這樣的:

  • 先執行 test 方法,拿到一個傳回值 0
  • 再執行 finally 中的代碼
  • 最後執行 return,把 0 給傳回

張小飛:明白了

8. try catch finally 的組合方式

諸小亮:下面給你介紹幾種 try catch finally 常用的組合方式

“第一種:try catch 單獨使用,這種最常見”

張小飛的Java之路——第十五章——異常

“第二種:一個try,多個catch”

張小飛的Java之路——第十五章——異常

“第三種:try 和 finally,沒有catch”

張小飛的Java之路——第十五章——異常

張小飛:“這樣也行?産生的異常時被誰處理了?”

諸小亮:“因為沒有catch,是以會自動把異常抛給調用者,但是 finally 中的代碼一定會執行”

9. 方法覆寫時的異常

諸小亮:在做繼承的時候,有幾點需要注意

第一:如果父類方法或父接口沒有 throws 異常,那麼子類複寫的方法也不能 throws 異常

class HeroException extends Exception{
}

class YaseException extends HeroException{
}

class DajiException extends Exception{
}

interface People{
    void run();
}
class Person{
    void eat(){
    }
}
class child extends Person implements People{
    //複寫父類的方法
    @Override
    void eat()throws HeroException {
        System.out.println("吃飯。。。。。");
    }

    //實作父接口的方法
    @Override
    public void run()throws HeroException {
        System.out.println("走。。。。。");
    }
}           

張小飛:上面的代碼,編譯失敗了

張小飛的Java之路——第十五章——異常

第二:子類方法聲明的異常類型,隻能是父類聲明的異常類型或者其子類型,比如:

張小飛的Java之路——第十五章——異常

張小飛:您的意思是,子類複寫後 run 方法後,聲明的異常隻能是 HeroException 或 其子類型?

諸小亮:是的,準确來說有 3 種方式可以選擇

第一種:聲明 HeroException

張小飛的Java之路——第十五章——異常

第二種:聲明 HeroException 的子類型

張小飛的Java之路——第十五章——異常

第三種:不聲明異常

張小飛的Java之路——第十五章——異常

張小飛:如果是聲明其他類型的異常呢?

諸小亮:那麼會直接編譯失敗,比如:

張小飛的Java之路——第十五章——異常

張小飛:如果父類方法沒聲明異常呢?

諸小亮:這是最特殊的一種情況,子類複寫方法後,可以聲明 RuntimeException 或 Error ,比如:

張小飛的Java之路——第十五章——異常

繼續閱讀