這篇文章是我近期看了《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的執行順序。
也就是說try-catch-finally中,可以隻有try-catch,也可以隻有try-finally。
執行順序:try執行完整->catch不執行->finally執行
執行順序:try執行部分->跳轉catch捕獲處理->finally執行
執行順序:try執行部分->finally執行
從上面的執行順序可以看出,finally語句不管在哪種情況是一定會執行的。基于這個認識,現在我們再來分析。
先看案例,本案例來自《Effective java》,現在要關閉資源:
關閉一個資源還好,但是如果再添加第二個資源,代碼看起來就會一團糟了。
如果需要關閉的資源不僅種類多,而且數量也很多。那代碼可就太龐大了。現在對這種方式的缺點進行一波總結:
1. 關閉的資源多事,代碼複雜
2. 對于第一個案例,如果裝置出現異常,那麼那麼調用readLine就會抛出異常,同時close方法也出現異常,在這種情況下,close異常會完全抹去readLine異常。在異常堆棧軌迹中也完全沒有readLine異常的記錄。
現在來測試一邊:

基于以上原因,出現了try-with-resources。
try-with-resources是在jdk1.7引入的,可以完美解決以上的問題。要使用這個構造的資源,必須先實作AutoCloseable接口,其中包含了單個傳回void的close方法,Java類庫與第三方類庫中的許多類和接口,現在都實作或擴充了AutoCloseable接口,是以我們現在不必實作了。
既然try-with-resources能夠解決以上的問題,現在來看一下,如何解決的:
可以看出這種方式代碼更加簡單,出現了錯誤,也能快速定位。
如果readLine和不可見的close方法都抛出異常,close方法抛出的異常就會被禁止,try-finally處理機制中我們無法看到,堆棧軌迹中也不能列印,但是try-with-resources不一樣,全部會被列印在堆棧軌迹中,并注明它們是被禁止的異常,通過編寫調用getSuppressed方法還可以通路到它們。現在再來測試一遍。
OK,上面基本上全部分析完畢,但是此書還給出了一個更好的案例:
這個firstLineOfFile方法沒有抛出異常,但是如果它無法打開檔案,或者無法從中讀取,就會傳回一個預設值。
處理必須關閉的資源時,始終要優先考慮使用try-with-resources,而不是try-finally。這樣得到的代碼将更簡潔,清晰,産生的異常也更有價值,這些也是try-finally無法做到的。