1. 程式人生 > 實用技巧 >Java7的try-with-resources宣告(轉)

Java7的try-with-resources宣告(轉)

看《Effective Java》第三版的時候,看到了其中建議將try-finally替換為try-with-resources。這個語法糖還算有意思,特此成文。

用法辨析

Java庫中有很多資源需要手動關閉,比如InputStream、OutputStream、java.sql.Connection等等。在此之前,通常是使用try-finally的方式關閉資源;Java7之後,推出了try-with-resources宣告來替代之前的方式。 try-with-resources 宣告要求其中定義的變數實現 AutoCloseable 介面,這樣系統可以自動呼叫它們的close方法,從而替代了finally中關閉資源的功能。

舉個栗子,下面是一個複製檔案的方法,按照原本try-catch-finally的寫法:

// 一個簡單的複製檔案方法。
public static void copy(String src, String dst) {
    InputStream in = null;
    OutputStream out = null;
    try {
        in = new FileInputStream(src);
        out = new FileOutputStream(dst);
        byte[] buff = new byte[1024];
        
int n; while ((n = in.read(buff)) >= 0) { out.write(buff, 0, n); } } catch (IOException e) { e.printStackTrace(); } finally { if (in != null) { try { in.close(); } catch (IOException e) { e.printStackTrace(); } }
if (out != null) { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }

可以看出,這種實現非常的醜陋。

下面來看使用了try-with-resources後的效果:

public static void copy(String src, String dst) {
    try (InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dst)) {
        byte[] buff = new byte[1024];
        int n;
        while ((n = in.read(buff)) >= 0) {
            out.write(buff, 0, n);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}

try-with-resources將會自動關閉try()中的資源,並且將先關閉後宣告的資源。

如果不catch IOException就更加清爽了:

public static void copy(String src, String dst) throws IOException {
    try (InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dst)) {
        byte[] buff = new byte[1024];
        int n;
        while ((n = in.read(buff)) >= 0) {
            out.write(buff, 0, n);
        }
    }
}

原理分析

那麼try-with-resources有什麼神奇之處呢?到底做了什麼呢?

我們先來看下AutoCloseable介面:

public interface AutoCloseable {
    void close() throws Exception;
}

其中僅有一個close方法,實現AutoCloseable介面的類將在close方法中實現其關閉資源的功能。

try-with-resources其實是個語法糖,它將在編譯時編譯成關閉資源的程式碼。我們將上述例子中的程式碼編譯成class檔案,再反編譯回java檔案,就能看到如下程式碼

public static void copy(String var0, String var1) throws IOException {
    FileInputStream var2 = new FileInputStream(var0);
    Throwable var3 = null;

    try {
        FileOutputStream var4 = new FileOutputStream(var1);
        Throwable var5 = null;

        try {
            byte[] var6 = new byte[1024];

            int var7;
            while((var7 = var2.read(var6)) >= 0) {
                var4.write(var6, 0, var7);
            }
        } catch (Throwable var29) {
            var5 = var29;
            throw var29;
        } finally {
            if (var4 != null) {
                if (var5 != null) {
                    try {
                        // 關閉FileOutputStream
                        var4.close();
                    } catch (Throwable var28) {
                        var5.addSuppressed(var28);
                    }
                } else {
                    var4.close();
                }
            }

        }
    } catch (Throwable var31) {
        var3 = var31;
        throw var31;
    } finally {
        if (var2 != null) {
            if (var3 != null) {
                try {
                    // 關閉FileInputStream
                    var2.close();
                } catch (Throwable var27) {
                    var3.addSuppressed(var27);
                }
            } else {
                var2.close();
            }
        }

    }

}

除卻處理異常相關的程式碼,其實就是呼叫了資源的close方法。

不過不得不說這個語法糖挺甜,真香。