[Java學習筆記] Java異常機制(也許是全網最獨特視角)

2022-07-16 12:00:27

Java 異常機制(也許是全網最獨特視角)

一、Java中的「異常「指什麼

  • 什麼是異常

    一句話簡單理解:異常是程式執行中的一些異常或者錯誤。

    (純字面意思)

  • Error類 和 Exception類

    Java中「萬物皆物件」,異常也不例外,

    Java把異常當做物件來處理,並將異常分為兩大類——Error(錯誤)和Exception(異常),它們都是Throwable類的子類。

    這裡看起來可能有點奇怪,什麼叫「把異常分為錯誤和異常兩類」??可以這樣粗暴地理解——異常有兩大類,一類是錯誤異常(Error),另一類是異常異常(Exception)

    至於為什麼這麼奇怪,我覺得既有翻譯的問題,也有Java檔案沒說清楚的問題。(主要是翻譯的鍋)

    Oracle的JavaSE官方檔案是這樣說的:

    An Error is a subclass of Throwable that indicates serious problems that a reasonable application should not try to catch. Most such errors are abnormal conditions.

    The class Exception and its subclasses are a form of Throwable that indicates conditions that a reasonable application might want to catch.

    所以個人見解,我們可以把Throwable這個類翻譯為「事故類 」(單詞problem:問題、狀況、事故),這個「事故類」的物件具有可被丟擲的性質,且」事故類「Throwable有兩個子類「錯誤事故」(Error,對應致命的大事故)和"異常事故"(Exception,對應剩餘可以處理的事故)。

    所以Java的「異常機制」這個翻譯很容易誤導人,讓人奇怪什麼叫異常包括錯誤和異常,EXception的異常和「異常機制」的異常,這兩個翻譯衝突了,所以我認為更應該翻譯為Java的「事故機制」。

    Error類的常見子類有IOError、AWTError、VirtualMachineError等,

    Exception類的常見子類有IOException、RuntimeException等。

  • Error和Exception的區別

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

    個人總結:曾經糾結Error和Exception的區別糾結了好久,後來發現根本不用糾結,就當成一樣的東西就好了,都是「意外狀況」,只不過一個嚴重點一個不嚴重點而已。由於Error是嚴重的異常,對於程式已經致命了,所以並不需要捕獲(catch)或者宣告(throws),而Exception屬於不那麼嚴重,還可以「挽救」或者說「預判」的異常,所以可以被捕獲(catch)或者宣告(throws)來做出進一步處理。


二、異常的捕獲和丟擲

  • 例外處理的五個關鍵字——try、catch、finally、throw、throws。

    • try 和 catch 關鍵字可以捕獲異常。

      try/catch 程式碼塊放在異常可能發生的地方,try 程式碼塊中的程式碼會先被執行,catch 語句包含要捕獲異常型別的宣告。當 try 語句中的程式碼發生一個異常時,try 後面的 catch 塊就會被檢查,如果發生的異常是catch語句所宣告的異常型別的範例,則該異常會被傳遞到該 catch 塊並執行catch程式碼塊中的程式碼,類似於傳遞一個引數到方法中。

      finally程式碼塊出現在catch程式碼塊之後,無論try程式碼塊中是否發生異常,finally程式碼塊中的程式碼總會被執行。

      注意:1、try程式碼塊後必須有catch程式碼塊或者finally程式碼塊;

      ​ 2、靠上的catch所宣告的異常型別不能是靠下的catc所宣告的異常型別的父類別(如以下程式碼中,e1不是e2的父類別)。

      try{
          //正常執行的程式碼
      }catch(Exception1 e1){
          //捕獲到Exception1類的異常後執行的程式碼
          //捕獲到的異常被賦值給e1
      }catch(Exception2 e2){
          //捕獲到Exception2類的異常後執行的程式碼
        //捕獲到的異常被賦值給e2
      }finally{
          //善後程式碼
      }
      
    • throw 和 throws 關鍵字用於主動丟擲異常。

      兩個關鍵字的區別是,throw用於丟擲一個異常,而throws用於宣告方法可能丟擲的異常。

      throw只能丟擲一個異常物件,throws可以宣告多個可能發生的異常類。

      即一個負責丟擲,一個負責宣告。

      注意:1、若方法中有 異常(嚴格來說是檢查異常,詳見後文)丟擲,必須使用throws語句在方法頭處宣告異常,或者在方法體內使用try/catch語句將異常丟擲語句包圍(將throw語句置於try程式碼塊中);

      ​ 2、若一個方法使用了throws語句宣告異常,則參照此方法的另一個方法必須使用throws語句在方法頭處宣告異常,或者使用try/catch語句將參照此方法的語句包圍(將參照此方法的語句置於try程式碼塊中)

      public void test1(){
          //使用try/catch語句處理異常
          try{
              throw new Exception();
          }catch(Exception e){
              System.out.println("Thers's an exception.")
          }
      }
      
      public void test2() throws Exception{
          //使用throws語句在方法頭處宣告異常
          throw new Exception();
      }
      
      public void test3() throws Exception{
          //參照test2也要宣告異常
          test2();
      }
      
      public void test4() throws Exception{
          //參照test2也要宣告異常
          
      

      總結:可以這樣理解,throw就是丟擲異常,try/catch就是處理異常,throws就是暫時不處理、交給參照自己的方法處理。

  • 檢查異常(checked exception)和非檢查異常(unchecked exception)

    檢查異常:除了Error類及其子類 和 RuntimeException類及其子類,其它的Throwable子類都是檢查異常。

    非檢查異常:Error類及其子類 和 RuntimeException類及其子類。

    檢查異常是編譯器要求程式必須處置的異常,這種異常的特點是Java編譯器會檢查它是否被捕獲(try/catch)或者宣告(throws),否則編譯不通過。非檢查異常是編譯器不要求強制處理的異常。

    public viod test(){
        //丟擲Error類物件但並不做處理,編譯也能通過。
        throw new Error();
    }
    public viod test(){
        //丟擲RuntimeException類物件但並不做處理,編譯也能通過。
        throw new RuntimeException();
    }
    //Error類及其子類 和 RuntimeException類及其子類 都是非檢查異常,
    
  • 執行時異常和非執行時異常

    執行時異常:RuntimeException類及其子類。

    非執行時異常:RuntimeException以外的Exception類及其子類。

    其實執行時異常就是 除去Error類的非檢查異常,

    如果我們不去處理執行時異常,JVM會接管處理,系統會把異常一直往上層拋,一直到最上層,

    最上層丟擲之後,如果丟擲異常線上程中,這個執行緒就會退出,如果丟擲異常在主程式中,整個程式就退出了。

    也就是說,如果不對執行時異常進行處理,程式會通過編譯並執行,出現執行時異常後,要麼是執行緒終止,要麼是主程式終止。

    public class Draft {
        public static void test1(){
            throw new RuntimeException();
        }
        public static void test2(){
            test1();
        }
        public static void main(String[] args) {
            test2();
        }
    }
    

    執行以上程式碼,終端會出現以下結果,

    Exception in thread "main" java.lang.RuntimeException
    at Draft.test1(Draft.java:3)
    at Draft.test2(Draft.java:6)
    at Draft.main(Draft.java:9)

三、自定義異常

  • 如果Java提供的內建異常型別不能滿足程式設計的需求,我們可以自定義異常類,只需要繼承Exception類或者它的子類,以上異常機制對自定義的異常類同樣適用。

呼,終於寫完咯!人生第一篇部落格,歡迎各路大佬指正!

此後會持續更新學習筆記,每一篇都會用心去寫去感悟。