'try(A a = new A())' VS 'try finally'

  • 2019 年 11 月 23 日
  • 筆記

實現了AutoCloseable介面的類,可以在try的時候直接實例化對象。try程式碼塊完成之後,自動調用close方法,相當於在finally里主動調用。但是出現異常後的流程和try finally有什麼不同呢? 下面寫程式碼測試一下。

首先定義一個類Cat,實現AutoCloseable介面

class Cat implements AutoCloseable{      void sayHello() throws Exception {          Utils.println("calling sayHello(), I will throw an exception");          throw new Exception("Exception in sayHello() ");      }        @Override      public void close() throws Exception {          Utils.println("I'm closing, I will throw an exception");          throw new Exception("Exception in close()");      }  }

我們的這個Cat有以下特點:

  • sayHello方法會拋出異常
  • close方法也會拋出異常

test v1: 'try(Cat cat = new Cat())' VS 'try finally'

沒有catch(不要被外層的catch迷惑,那只是為了列印異常)

static void testV1(){      Utils.println("----try(Cat cat = new Cat())-----");      try{          try(Cat cat = new Cat()){              cat.sayHello();          }        }catch (Exception e){          Utils.println("cache error in main (" + e + "), let's see its stack trace");          Utils.printStackTrace(e);      }      Utils.println("--------------");        Utils.println("----try finally-----");      try{          Cat cat = null;          try{              cat = new Cat();              cat.sayHello();          }finally {              if(cat != null){                  cat.close();              }          }      }catch (Exception e){          Utils.println("cache error in main (" + e + "), let's see its stack trace");          Utils.printStackTrace(e);      }      Utils.println("--------------");  }

結果輸出:

----test v1----------------------------------------  ----try(Cat cat = new Cat())-----  calling sayHello(), I will throw an exception  I'm closing, I will throw an exception  cache error in main (java.lang.Exception: Exception in sayHello() ), let's see its stack trace  java.lang.Exception: Exception in sayHello()      at Cat.sayHello(Cat.java:4)      at Test.testV1(Test.java:16)      at Test.main(Test.java:4)      Suppressed: java.lang.Exception: Exception in close()          at Cat.close(Cat.java:10)          at Test.testV1(Test.java:17)          ... 1 more  --------------  ----try finally-----  calling sayHello(), I will throw an exception  I'm closing, I will throw an exception  cache error in main (java.lang.Exception: Exception in close()), let's see its stack trace  java.lang.Exception: Exception in close()      at Cat.close(Cat.java:10)      at Test.testV1(Test.java:33)      at Test.main(Test.java:4)  --------------

結論

  • try(Cat cat = new Cat())
    • try程式碼塊完成之後會自動調用close
    • close拋出的異常,被Suppressed了,外層捕獲的只有sayHello的異常,但通過堆棧可以找到這個Suppressed的異常
  • try finally
    • 外層捕獲的是在finally執行close時拋出的異常,sayHello的異常完全不見了。

test v2: 'try(Cat cat = new Cat()) catch{}' VS 'try catch finally'

有catch,並且catch里再拋出異常

static void testV2(){      Utils.println("----try(Cat cat = new Cat()) catch-----");      try{          try(Cat cat = new Cat()){              cat.sayHello();          } catch (Exception e) {              Utils.println("cached err (" + e.getMessage() + "), I will throw an exception again");              throw new Exception("Exception in catch", e);          }        }catch (Exception e){          Utils.println("cache error in main (" + e + "), let's see its stack trace");          Utils.printStackTrace(e);      }      Utils.println("-----------------------------------------");        Utils.println("----try catch finally--------------------");      try{          Cat cat = null;          try{              cat = new Cat();              cat.sayHello();          } catch (Exception e) {              Utils.println("cached err (" + e.getMessage() + "), I will throw an exception again");              throw new Exception("Exception in catch", e);          }finally {              if(cat != null){                  cat.close();              }          }      }catch (Exception e){          Utils.println("cache error in main (" + e + "), let's see its stack trace");          Utils.printStackTrace(e);      }      Utils.println("-------------------------------------------");  }

結果輸出

----test v2------  ----try(Cat cat = new Cat()){} catch{}-----  calling sayHello(), I will throw an exception  I'm closing, I will throw an exception  cached err (Exception in sayHello() ), I will throw an exception again  cache error in main (java.lang.Exception: Exception in catch), let's see its stack trace  java.lang.Exception: Exception in catch      at Test.testV2(Test.java:50)      at Test.main(Test.java:8)  -----------------------------------------  ----try catch finally--------------------  calling sayHello(), I will throw an exception  cached err (Exception in sayHello() ), I will throw an exception again  I'm closing, I will throw an exception  cache error in main (java.lang.Exception: Exception in close()), let's see its stack trace  java.lang.Exception: Exception in close()      at Cat.close(Cat.java:10)      at Test.testV2(Test.java:70)      at Test.main(Test.java:8)  -------------------------------------------  ---------------------------------------------------------------------

結論

  • try(Cat cat = new Cat()) catch
    • catch之前就調用了close(符合try程式碼塊完成之後會自動調用close這個結論)
    • catch到的是sayHello的異常,close拋出的異常依然被Suppressed了
    • catch中再次拋出的異常被外層捕獲
  • try catch finally
    • 先走catch,再走finally,所以catch捕獲的是sayHello的異常
    • catch中再次拋出的異常不見了,外層捕獲的是在finally執行close時拋出的異常。

測試程式碼地址:https://github.com/kongxiangxin/pine/tree/master/auto-closeable