為什麼推薦使用try-with-resources代替try-finally

2020-12-22 愚公要移山1

這篇文章是我近期看了《Effective java》一書中總結的,來自其中第九條。為了對其理解的更加透徹,因此重新分析了一下,並加入了一些其他點。

本文的所有例子均在本地代碼運行完畢基於JDK版本1.8,運行環境eclipse本文類名:TryWithResources,下文的堆棧信息也以此為基礎

在java開發中,一些網絡連結或者是文件資源都需要程式設計師去手動調用close方法關閉,比如InputStream、OutputStream和java.sql.Connection。如果忘關了就可能造成嚴重的性能後果。而關閉的方法有很多種。比如finalizer、try-catch-finally、try-with-resources等等。

finalizer機制可以關閉,但是其執行性不可預測,還有可能造成內存洩漏,所以一般不使用,雖然java9還提出了cleaner機制代替了finalizer機制,但是其執行依然不可預測,因此選擇就落在了try-catch-finally和try-with-resources之間。

本文就是為了討論該選擇哪一種比較好,不過題目已經給出了答案肯定是try-with-resources。下面帶著這個答案去分析為什麼推薦使用try-with-resources而不是try-finally。

一、前言

在正式分析之前,我們先看一波finally的執行順序。

1、finally不是必要條件

也就是說try-catch-finally中,可以只有try-catch,也可以只有try-finally。

2、假設基於try-catch-finally:

第一:代碼沒有異常

執行順序:try執行完整->catch不執行->finally執行

第二:代碼有異常且catch進行捕獲**

執行順序:try執行部分->跳轉catch捕獲處理->finally執行

第三:代碼有異常且catch不捕獲:這種情況沒有catch**

執行順序:try執行部分->finally執行

從上面的執行順序可以看出,finally語句不管在哪種情況是一定會執行的。基於這個認識,現在我們再來分析。

二、try-finally的缺點

先看案例,本案例來自《Effective java》,現在要關閉資源:

static String firstLineOfFile(String path)throws IOException {BufferedReader reader = new BufferedReader(new FileReader(path));try {return reader.readLine(); } finally { reader.close(); }}

關閉一個資源還好,但是如果再添加第二個資源,代碼看起來就會一團糟了。

staticvoidcopy(String src, String desc)throws IOException {InputStream in = new FileInputStream(src);try { OutputStream out = new FileOutputStream(desc);byte[] bytes = newbyte[1024];int n;try {while ((n = in.read(bytes)) != -1) { out.write(bytes, 0, n); } } finally { out.close(); } } finally { in.close(); }}

如果需要關閉的資源不僅種類多,而且數量也很多。那代碼可就太龐大了。現在對這種方式的缺點進行一波總結:

1. 關閉的資源多事,代碼複雜

2. 對於第一個案例,如果設備出現異常,那麼那麼調用readLine就會拋出異常,同時close方法也出現異常,在這種情況下,close異常會完全抹去readLine異常。在異常堆棧軌跡中也完全沒有readLine異常的記錄。

現在來測試一邊:

基於以上原因,出現了try-with-resources。

三、try-with-resources的優勢

try-with-resources是在jdk1.7引入的,可以完美解決以上的問題。要使用這個構造的資源,必須先實現AutoCloseable接口,其中包含了單個返回void的close方法,Java類庫與第三方類庫中的許多類和接口,現在都實現或擴展了AutoCloseable接口,因此我們現在不必實現了。

既然try-with-resources能夠解決以上的問題,現在來看一下,如何解決的:

1、代碼複雜問題解決

staticvoidcopy(String src, String desc)throws IOException {try (InputStream in = new FileInputStream(src);OutputStream out = new FileOutputStream(desc)) {byte[] bytes = newbyte[1024];int n;while ((n = in.read(bytes)) != -1) { out.write(bytes, 0, n); } }}

可以看出這種方式代碼更加簡單,出現了錯誤,也能快速定位。

2、異常抹去問題解決

static String firstLineOfFil(String path)throws IOException {try (BufferedReader reader = new BufferedReader(new FileReader(path))) {return reader.readLine();}}

如果readLine和不可見的close方法都拋出異常,close方法拋出的異常就會被禁止,try-finally處理機制中我們無法看到,堆棧軌跡中也不能列印,但是try-with-resources不一樣,全部會被列印在堆棧軌跡中,並註明它們是被禁止的異常,通過編寫調用getSuppressed方法還可以訪問到它們。現在再來測試一遍。

OK,上面基本上全部分析完畢,但是此書還給出了一個更好的案例:

static String firstLineOfFile(String path, String defaultVal){try (BufferedReader reader = new BufferedReader(new FileReader(path))) {return reader.readLine();} catch (IOException e) {return defaultVal; }}

這個firstLineOfFile方法沒有拋出異常,但是如果它無法打開文件,或者無法從中讀取,就會返回一個默認值。

結論

處理必須關閉的資源時,始終要優先考慮使用try-with-resources,而不是try-finally。這樣得到的代碼將更簡潔,清晰,產生的異常也更有價值,這些也是try-finally無法做到的。

相關焦點

  • 為什麼不推薦使用try-catch-finally處理Java異常?
    比如finalizer、try-catch-finally、try-with-resources等等。finalizer機制可以關閉,但是其執行性不可預測,還有可能造成內存洩漏,所以一般不使用,雖然java9還提出了cleaner機制代替了finalizer機制,但是其執行依然不可預測,因此選擇就落在了try-catch-finally和try-with-resources之間。
  • java1.7 AutoCloseable接口和try-with-resources語法示例
    try-with-resources語句是JDK 1.7中一個新的異常處理機制,更方便簡潔的關閉在try-catch語句塊中使用的資源,這裡不得不提及傳統的資源釋放方式try-catch-finally:byte[] b = new byte[1024];FileInputStream
  • java的try、catch、finally
    這個時候就是java中的try、catch、finally大展身手的時候了。說到try、catch、finally,大家看看下面這段代碼,最後執行輸出的結果應該是什麼呢?帶著這個問題,我們往下看看吧。try對應的大括號包含的代碼塊中。
  • Java中如何通過try優雅地釋放資源?
    3.使用try-with-resources的好處在很久很久以前,你是不是和小明一樣每每操作輸入流、輸出流的時候,用的都是try-catch-finally代碼塊?現在,答應我好嘛,作為小明的小夥伴,看完這篇文章後,再寫代碼時,不要再將就,務必使用try-with-resources會更小明(優雅)一些。
  • Java中關於try、catch、finally中的細節分析
    ,接下來執行finally語句塊,把變量t賦值為finally,然後return t,則t的值是finally,最後t的值就是finally,程序結果應該顯示finally,但是實際結果為try。為什麼會這樣,我們不妨先看看這段代碼編譯出來的class對應的字節碼,看虛擬機內部是如何執行的。
  • Java 中關於 try、catch、finally 中的細節分析
    t,則t的值是finally,最後t的值就是finally,程序結果應該顯示finally,但是實際結果為try。為什麼會這樣,我們不妨先看看這段代碼編譯出來的class對應的字節碼,看虛擬機內部是如何執行的。
  • java中關於try、catch、finally中的細節分析
    ,接下來執行finally語句塊,把變量t賦值為finally,然後return t,則t的值是finally,最後t的值就是finally,程序結果應該顯示finally,但是實際結果為try。為什麼會這樣,我們不妨先看看這段代碼編譯出來的class對應的字節碼,看虛擬機內部是如何執行的。
  • 面試官:當return遇到try、catch、finally時會發生什麼?
    當try中帶有return時,會先執行return前的代碼,然後暫時保存需要return的信息,再執行finally中的代碼,最後再通過return返回之前保存的信息。所以,這裡方法返回的值是try中計算後的2,而非finally中計算後的10。當finally中帶有return的時候又會出現什麼結果呢?
  • 面試須知(8):C#的異常處理機制(try...catch...finally)
    在 C# 語言中異常與異常處理語句包括三種形式,即 try catch、try finally、try catch finally。在上述三種異常處理的形式中所用到關鍵字其含義如下:try:一個 try 塊標識了一個將被激活的特定的異常的代碼塊。後跟一個或多個 catch 塊。
  • 三道try-catch-finally題目,面試妥妥的
    try-catch-finally題目分析:1.執行 foo(0)時,不滿足 try 語句塊中的 if 語句,所以不會拋出異常,執行 finally 語句。try-catch-finally題目分析:1.進入try的時候temp=1,然後return,temp=2,值保存在臨時空間中。
  • Java面試題:try-catch-finally中return的用法
    執行結果從結果分析看,catch中的return前執行了finally,並且在執行finally前,return的值已經確定。總結如下:finally塊的語句在try或catch中的return語句執行之後返回之前執行,若finally裡也有return語句則覆蓋try或catch中的return語句直接返回;若finally中沒有return則返回try或者catch中的已確定的return值。
  • try-catch-finally中的4個巨坑,老程式設計師也搞不定!
    在 Java 語言中 try-catch-finally 看似簡單,一副人畜無害的樣子,但想要真正的「掌控」它,卻並不是一件容易的事。別的不說,咱就拿 fianlly 來說吧,別看它的功能單一,但使用起來卻「暗藏殺機」,若您不信,咱來看下面的這幾個例子...
  • 字節碼層面理解try、catch、finally
    面試中經常有關於try、catch、finally相關的問題,今天從字節碼層面了解他們的運行流程。字節碼指令分析前面30行之前的指令對應的是try-catch中間的代碼,把fileInputStream與serverSocket初始化出來放到方法的變量表中。
  • Java筆試面試總結—try、catch、finally語句中有return 的各類情況
    正文本篇文章主要是通過舉例的方式來闡述各種情況,我這裡根據 try-catch-finally 語法塊分為兩種大情況討論:try-catch 語法塊和 try-catch-finally 語句塊,然後再在每種情況裡再去具體討論。
  • js錯誤處理,"try..catch"
    然後將該對象作為參數傳遞給 catch:try {  // ...} catch(err) { // <-- 「error 對象」,也可以用其他參數名代替 err  // ...在這兩種情況下都會執行 finally 子句。變量和 try..catch..finally 中的局部變量請注意,上面代碼中的 result 和 diff 變量都是在 try..catch 之前 聲明的。否則,如果我們使用 let 在 try 塊中聲明變量,那麼該變量將只在 try 塊中可見。
  • python妙招-異常處理之try-except
    異常可以通過 try 語句來檢測. 任何在 try 語句塊裡的代碼都會被監測, 檢查有無異常發生。try 語句有兩種主要形式: try-except 和 try-finally . 這兩個語句是互斥的, 也就是說你只能使用其中的一種 .
  • 一文詳解 Try 和異常的區別
    try、catch、finally基本介紹1. trytry 語句是用來進行錯誤處理或者清理錯誤的代碼塊。try語句後面必須緊跟 catch 代碼塊或者 finally 代碼塊(也可以兩者都存在)。當 try 中的代碼發生錯誤時,如果存在catch代碼塊,那麼它將會被將會被執行,如果只存在 finally 代碼塊的話,他將在 try 代碼塊執行完畢後執行,如果存在 catch 代碼塊和 finally 代碼塊的話,finally 代碼塊將在 catch 代碼塊執行完畢後執行。
  • Try..Catch 不能捕獲的錯誤有哪些?注意事項又有哪些?
    今天的內容中,我們來學習一下使用try、catch、finally和throw進行錯誤處理。我們還會講一下 JS 中內置的錯誤對象(Error, SyntaxError, ReferenceError等)以及如何定義自定義錯誤。
  • 看完這篇 final、finally 和 finalize 和面試官扯皮就沒問題了
    也是一個關鍵字,不過我們可以使用 finally 和其他關鍵字結合做一些組合操作;finalize 是一個不讓人待見的方法,它是對象祖宗 Object 中的一個方法,finalize 機制現在已經不推薦使用了。
  • 談談final、finally、 finalize有什麼不同?
    finally 則是 Java 保證重點代碼一定要被執行的一種機制。我們可以使用 try-finally 或者 try-catch-finally 來進行類似關閉 JDBC 連接、保證 unlock 鎖等動作。finalize 是基礎類 java.lang.Object 的一個方法,它的設計目的是保證對象在被垃圾收集前完成特定資源的回收。