師爺,翻譯下什麼叫異常!

前言

人非聖賢,孰能無過,更況且是程序員.因此排錯也是我猿的一項基本技能.java

一.分析異常種類

排錯要先知道有哪些錯,先上圖,而後一項一項分析!程序員

1.都繼承於Throwable

這個類的接口基本決定着java全部異常的行爲,好比經常使用在catch中getMessage(),printStackTrace().其餘詳細以下編程

返回類型安全

方法名稱jvm

方法說明編輯器

Throwableide

fillInStackTrace()工具

在異常堆棧跟蹤中填充。post

《2020最新Java基礎精講視頻教程和學習路線!》學習

Throwable

getCause()

返回此 throwable 的 cause;若是 cause 不存在或未知,則返回 null。

String

getLocalizedMessage()

建立此 throwable 的本地化描述。

String

getMessage()

返回此 throwable 的詳細消息字符串。

StackTraceElement[]

getStackTrace()

提供編程訪問由 printStackTrace() 輸出的堆棧跟蹤信息。

Throwable

initCause(Throwable cause)

將此 throwable 的 cause 初始化爲指定值。

void

printStackTrace()

將此 throwable 及其追蹤輸出至標準錯誤流。

void

printStackTrace(PrintStream s)

將此 throwable 及其追蹤輸出到指定的輸出流。

void

printStackTrace(PrintWriter s)

將此 throwable 及其追蹤輸出到指定的 PrintWriter。

void

setStackTrace(StackTraceElement[] stackTrace)

設置將由 getStackTrace() 返回,並由 printStackTrace() 和相關方法輸出的堆棧跟蹤元素。

String

toString()

返回此 throwable 的簡短描述。

2.繼承上分類

  • error:系統級別的異常.該錯誤像運行時堆溢出,屬於代碼級不可控範疇.

    • 非受檢異常
  • exception:應用級異常,屬於代碼級可控異常.因此自定義的異常都須要繼承這個接口. 同時exception也包括以下兩類.

    • 運行時異常(非受檢異常)
    • 受檢異常

3.何爲受檢異常跟非受檢異常

  • 受檢異常:編譯器要檢查此類異常,也就是編輯器會主動爆紅提示增長try catch.
  • 非受檢異常:編譯器不檢查此類異常,實際開發時須要開發人員本身邏輯規避.

二.異常捕捉

家喻戶曉,java中經過try catch來捕捉異常,打到日誌裏爲排錯提供依據.下面對try catch的一些場景進行分析,照例先上一段案例代碼

public class TestTryCatch {
    public static void main(String[] args) {
        System.out.println(returnInt());
    }
    public static int returnInt(){
        int x;
        try {
            x = 1;
            return x;
        } catch (NullPointerException e) {
            x = 2;
            return x;
        } finally {
            x = 3;
        }
    }
}

1.經過字節碼查看returnInt()邏輯

jvm的try catch 邏輯是經過Exception table條件來實現的,在from到to的計數行數中爆出的異常從上向下比對type類型,符合的則修改程序計數器中的行數到target指示的行數.

public static int returnInt();
    descriptor: ()I
    flags: (0x0009) ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=4, args_size=0
         0: iconst_1   //取1放到棧頂                                                ---->是try塊中的開始
         1: istore_0   //從棧頂將數值放到本地變量(局部變量表第0個變量) 
         2: iload_0    //保存x到returnValue中                                      ---->是try塊中的結束
         3: istore_1                                                                                                                    ---->是finally塊中的開始(沒報錯)
         4: iconst_3   //取3放到棧頂    
         5: istore_0   //從棧頂將數值放到本地變量(局部變量表第0個變量)               ---->是finally塊中的結束(沒報錯)
         6: iload_1
         7: ireturn   //返回結果
         8: astore_1  //給catch中定義的Exception e賦值                              ---->是catch塊中的開始
         9: iconst_2  //catch塊中的x=2
        10: istore_0
        11: iload_0
        12: istore_2  //                                                         ---->是catch塊中的結束
        13: iconst_3  //finaly塊中的x=3                                           ---->是finally塊中的開始(catch到異常)
        14: istore_0  
        15: iload_2
        16: ireturn   //返回結果                                               ---->是finally塊中的結束(catch到異常)
        17: astore_3  //若是出現了不屬於java.lang.NullPointerException及其子類的異常纔會走到這裏 
        18: iconst_3  //finaly塊中的x=3                                      ---->是finally塊中的開始(沒catch到異常)
        19: istore_0    
        20: aload_3   //將異常放置到棧頂,並拋出
        21: athrow    //                                                    ---->是finally塊中的結束(沒catch到異常)
      Exception table:
         from    to  target type
             0     4     8   Class java/lang/NullPointerException
             0     4    17   any
             8    13    17   any

2.列出結果

同時從字節碼能夠看出有以下結果(都假設在x賦值後,return前報錯):

  1. 若是try中沒有bug,代碼會先執行try中的x=1而後執行finally中的x=3,return 3
  2. 若是try中有bug,且被catch到,catch中無異常的話會先執行x=1,而後再執行catch中的x=2,最後執行finally中的x=3,return 3
  3. 若是try中有bug,且被catch到,catch中有異常的話會先執行x=1,而後再執行catch中的x=2,後執行finally中的x=3,最後拋出異常
  4. 若是try中有bug,可是沒有被catch到則執行 x=1,而後執行x = 3,最後拋出異常

3.得出結論

從上能夠得出結論

  • 捕獲異常與拋異常,必須是徹底匹配,或者捕獲異常是拋異常的父類
  • 須要考慮到finally最終會執行,須要考慮try中對象會被修改的問題.
  • 不要在 finally 塊中使用 return. 通過測試若是finally中加了retrun x;則字節碼中不會出現athrow,而是變爲以下ireturn,因此使用的時候須要注意finally中若是有return那麼異常將不會被拋出.

    ......
        23: astore_3
            24: iconst_3
            25: istore_0
            26: iload_0
            27: ireturn
          Exception table:
             from    to  target type
                 0     7    11   Class java/lang/NullPointerException
                 0     7    23   any
                11    19    23   any

    在阿里泰山版開發手冊中也有說明.

  • try catch中的邏輯都是不安全的,都有可能中間跳出,可是finally確定會執行,因此能夠在finally中將資源對象、流對象進行關閉
  • 字節碼中Exception table能夠看出:捕獲異常與拋異常,必須是徹底匹配,或者捕獲異常是拋異常的父類,否則邏輯進不到catch當中

三.異常小工具

1.addSuppressed

例如這樣一個場景:try中出現了報錯,可是進入到finally執行關閉io的時候也報了錯,那麼結果是方法只會拋出finally中報的錯.addSuppressed能夠用來解決這個問題,能夠同時將try中的錯誤跟finally中的錯誤都拋出.代碼舉例以下:

public static int returnInt() throws IOException{
        IOException e = null;
        int x = 0;
        try {
            x = 1;
              dothing();
            return x;
        }catch (IOException e1){
            e = e1;
            x = 2;
            return x;
        }finally {
            x = 3;
            try {
                dothing();
            }catch (IOException e2){
                if(e != null){
                    //注意這裏
                    e.addSuppressed(e2);
                }else{
                    e = e2;
                }
            }
            if(e != null){
                throw e;
            }
        }
    }

2.try-catch-resource

不止各位看官是否以爲上面addSuppressed的書寫方法特別的繁瑣呢,1.7版本的try-catch-resource經過讓資源實現AutoClosable中的close來實現無需手寫,自動調用關閉邏輯的功能;舉例代碼:

public class Connection implements AutoCloseable {
  public void sendData() {
    System.out.println("正在發送數據");
  }
  @Override
  public void close() throws Exception {
    System.out.println("正在關閉鏈接");
  }
}
---------------------------------------------------
public class TryWithResource {
  public static void main(String[] args) {
    try (Connection conn = new Connection()) {
      conn.sendData();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  }
}

而後將代碼放在idea裏面查看反編譯代碼

public class TryWithResource {
    public TryWithResource() {
    }

    public static void main(String[] args) {
        try {
            Connection conn = new Connection();
            Throwable var2 = null;

            try {
                conn.sendData();
            } catch (Throwable var12) {
                var2 = var12;
                throw var12;
            } finally {
                if (conn != null) {
                    if (var2 != null) {
                        try {
                            conn.close();
                        } catch (Throwable var11) {
                            var2.addSuppressed(var11);
                        }
                    } else {
                        conn.close();
                    }
                }

            }
        } catch (Exception var14) {
            var14.printStackTrace();
        }

    }
}

看吧,原來try-catch-resource就是實質上try-catch-finally加addSuppressed的組合

3.lombok的@SneakyThrows

加入有場景不須要精細判斷,而是須要梭哈異常的狀況下這個註解能夠很方便的幫助你自動生成try-catch,以下代碼

import lombok.SneakyThrows;

/**
 * @author wuzt
 */
public class TryWithResource {
    @SneakyThrows
    public static void main(String[] args) {
        Connection conn = new Connection();
        conn.sendData();
    }
}

使用idea反編譯後:

public class TryWithResource {
    public TryWithResource() {
    }

    public static void main(String[] args) {
        try {
            Connection conn = new Connection();
            conn.sendData();
        } catch (Throwable var2) {
            throw var2;
        }
    }
}

由上發現,其實這個註解的意義就是拋出全部異常

應用的的場景比較符合的像是阿里開發手冊中的以下

結束

求各位看客老爺們點個贊再走啊.

連接:https://juejin.cn/post/690186...

相關文章
相關標籤/搜索