首頁>技術>

這篇文章是我近期看了《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之間。

一、前言

在正式分析之前,我們先看一波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();        }}

關閉一個資源還好,但是如果再新增第二個資源,程式碼看起來就會一團糟了。

static void copy(String src, String desc) throws IOException {        InputStream in = new FileInputStream(src);        try {            OutputStream out = new FileOutputStream(desc);            byte[] bytes = new byte[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、程式碼複雜問題解決
static void copy(String src, String desc) throws IOException {        try (InputStream in = new FileInputStream(src);             OutputStream out = new FileOutputStream(desc)) {            byte[] bytes = new byte[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無法做到的。

14
最新評論
  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Nginx實現高速併發處理的原理詳解