Day27:異常詳解

異常

1.1 異常概述

異常(Exception)指程序運行中出現的不正常情況:文件找不到、網絡異常、非法參數等等。

我們通過代碼來了解一下:

public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        method(a);
    }
    public static void method(int[] a){
        Sytem.out.println(a[3]);
    }
}
//輸出結果
ArrayIndexOutOfBoundsException//數組下標越界

上面我們得到異常中一個非常常見的異常:數組下標越界異常。

1.1.1 異常體系

Exception也是一個類,throwable是所有異常的超類;throwable還有一個子類是錯誤Error

Error:表示很嚴重的問題,無需處理。

Exception:稱為異常類,表示程序本身可以處理的問題。

  • RuntimeException:在編譯期是不檢查的,出現問題後需要我們會來修改代碼;(也就是說我們在寫代碼的過程中程序是不會報錯的)
  • 非RuntimeException:在編譯期就必須處理的問題,如果不處理,將不能通過編譯,程序更加不會運行;(也就是寫代碼爆紅的時候)

1.2 JVM的默認處理方案

當我們的程序出現異常的時候,如果我們不去處理,Java中的虛擬機會執行默認處理方案。

public class demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        System.out.println("開始");
        method(a);
        System.out.println("結束");
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}
//輸出結果
開始
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3//異常出現的類型
	at com.liuwei.oop.exception.demo.method(demo.java:11) //異常出現的位置
	at com.liuwei.oop.exception.demo.main(demo.java:7)
    //結束這個輸出語句並沒有執行

如果我們不處理異常,交給系統去處理會得到異常的原因、異常的位置輸出在控制台,且終止程序運行

1.3 異常處理

我們為什麼需要處理異常呢?

因為交給Java默認處理的話,會終止我們的程序運行!如果我們自己將異常處理了,可以讓程序保持運行。

處理異常的方案:

  • try—catch—
  • throw

1.3.1 try—catch—

格式:

try{
    可能出現異常的代碼;
}catch(異常的類型   變量名){
    如與catch中的異常類型匹配成功後需要執行的異常處理代碼;
    
}

執行流程:

程序執行try內部的代碼,判斷是否有異常,

若出現異常則將異常實例化

將異常的類型與catch中的異常類型進行匹配

匹配成功後,執行異常處理代碼

程序執行後續代碼…..


我們去代碼中學習一下try—catch—的使用:

public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        System.out.println("開始");
        try{
            method(a);//需要檢查異常的代碼
            //有異常是會實例化異常並與catch中的異常類型進行匹配,匹配成功執行異常處理代碼
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println("數組下標訪問錯誤");//異常的處理代碼
        }
        System.out.println("結束");
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}
//輸出結果
開始
數組下標訪問錯誤
結束

通過上面的代碼運行,我們看到我們捕獲異常且處理後,後續的代碼依然被執行了;

這也就是我們處理異常的初衷:出現異常後不影響後面代碼的執行。

但是有需要我們注意的地方是異常處理代碼一般來說不是輸出一句話,而是要顯示異常原因及位置的信息;

我們在捕獲異常時實例化了對象,我們可以在異常處理代碼的地方調用異常的一些方法來顯示我們想要的異常原因及位置的信息。

public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        System.out.println("開始");
        try{
            method(a);
        }catch(ArrayIndexOutOfBoundsException e){
           // System.out.println("數組下標訪問錯誤");//異常的處理代碼
            e.printStackTrace();//我們調用了異常的方法,將錯誤信息打印出來
        }
        System.out.println("結束");
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}
//輸出結果
開始
結束
java.lang.ArrayIndexOutOfBoundsException: 3
	at com.liuwei.oop.exception.demo.method(demo.java:16)
	at com.liuwei.oop.exception.demo.main(demo.java:8)

我們發現我們的異常處理得到的跟Java異常默認處理方案是一樣的,但是僅僅是顯示的內容一樣,而我們的程序並沒有終止。


1.4 Throwable的成員方法

成員方法 說明
public String getMessage() 返回此throwable的詳細消息字符串
public String toString() 返回此可拋出的簡短描述
public void printStackTrace() 把異常的錯誤信息輸出在控制台

我們在代碼里對三種方法進行了解:

  • public String getMessage()
public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        System.out.println("開始");
        try{
            method(a);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e.getMessage());
        }
        System.out.println("結束");
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}
//輸出結果
開始
3//getMessage();會返回出現異常的原因
結束

我們打開getMessage()的源代碼:

發現getMessage();Throwable類的一個方法;且getMessage();中返回了一個detailMessage

detailMessageThrowable類的一個成員變量

也就是說我們調用getMessage();時返回給我們的是detailMessage,而detailMessageThrowable類的屬性,也就意味着detailMessageThrowable類的有參構造中被賦值了,而這個值正是其子類自身調用構造器時傳來的;

try捕捉到異常時會創建該異常的對象,此時該異常類會調用有參構造器,且調用其父類的有參構造器,其父類再去調用父類,最終調到Throwable類的有參構造器對detailMessage進行賦值,最後我們調用getMessage();並返回detailMessage的值。

//簡化描述下Throwable有參的構造和getMessage()方法的調用
public class Throwable{
    private String detailMessage;
    
    public Throwable(String message){//當異常類的有參構造調動牽動了Throwable的有參構造器的調用,且將 detailMessage初始化了
        detailMessage=message;
    }
    
    public String getMessage(){
        return detailMessage;
    }
}
  • public String toString()
public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        System.out.println("開始");
        try{
            method(a);
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e.toString());
        }
        System.out.println("結束");
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}
//輸出結果
開始
java.lang.ArrayIndexOutOfBoundsException: 3//輸出了異常類型以及原因;包含了getMessage();的內容
結束
  • public void printStackTrace()
public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
        System.out.println("開始");
        try{
            method(a);
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
        System.out.println("結束");
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}
//輸出結果
開始
結束
java.lang.ArrayIndexOutOfBoundsException: 3
	at com.liuwei.oop.exception.demo.method(demo.java:16)
	at com.liuwei.oop.exception.demo.main(demo.java:8)
    //輸出了異常的原因,類型,位置
    //printStackTrace();輸出的信息是最全的,一般我們使用這個

1.5 編譯時異常和運行時異常的區別

Java中的異常有兩大類:編譯時異常和運行時異常;也被稱為受檢異常和非受檢異常;

所有的RuntimeException及其子類都被成為運行時異常,除外其他的異常都為編譯時異常

  • 編譯時異常:必須顯示處理,否則程序會發生錯誤,編譯無法通過;
  • 運行時異常:無需顯示處理,需要我們修改代碼;也可以和編譯時異常 一樣處理
public class Demo{
    public static void main(String[] args){
        int[] a={1,2,3};
            method(a);//ArrayIndexOutOfBoundsException,需要我們修改代碼
    }
    public static void method(int[] a){
        System.out.println(a[3]);
    }
}

類似我們上面的代碼在編譯時沒有報錯,而運行時報錯,就是運行時異常。

編譯時異常怎麼處理?

也就是我們前面講的try catch


1.6 異常處理之throws

我們前面已經知道了try catch方式來處理異常,但是並不是所有的異常情況我們都有權限通過try catch方式來處理;

Java提供了另外一種處理方式:throws處理方案

格式:

throw 異常類名;

需要注意的是throws 異常類名;是跟在方法聲明的括號之後的!

throws 異常類名僅僅是拋給了調用者,並沒有解決異常,如果要解決還得用try catch

//運行時異常的throws處理
public class Demo{
    public static void main(String[] args){
        //method();//此時throws將異常拋到此處,我們異常並沒有解決,我們還是需要處理異常,即用try  catch方式來處理
        
        try{
            method();
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }
    }
    public static void method1() throws ArrayIndexOutOfBoundsException{//throws僅僅是向調用者拋出異常,並不能解決異常
        int[] a={1,2,3};//我們是可以通過try catch來處理的,這裡演示throws處理
        System.out.println(a[3]);
    }
}

總結:

  • 編譯時異常必須進行處理:兩種處理方案:try catchthrows, throws要注意誰調用誰就要處理異常
  • 運行時異常可以不處理:我們始終要回來修改代碼