天天看點

使用try-with-resources優雅關閉資源

推薦:​​Java網絡程式設計彙總​​

使用try-with-resources優雅關閉資源

JDK1.7之後,引入了​

​try-with-resources​

​​,使得關閉資源操作無需層層嵌套在​

​finally​

​​中,代碼簡潔不少,本質是一個文法糖,能夠使用​

​try-with-resources​

​​關閉資源的類,必須實作​

​AutoCloseable​

​接口。

JDK1.7版本之前,傳統的關閉資源操作如下:

public static void main(String[] args){

    FileInputStream fileInputStream = null;
    try {
        fileInputStream = new FileInputStream("file.txt");
        fileInputStream.read();
    } catch (IOException e) {
        e.printStackTrace();
    }finally {
        try {
            assert fileInputStream != null;
            fileInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}      

可以看到,為了確定資源關閉正常,需要​

​finally​

​​中再嵌入​

​try-catch​

​​,​

​try-catch​

​​中打開資源越多,​

​finally​

​嵌套越深,可能會導緻關閉資源的代碼比業務代碼還要多。

但是使用了​

​try-with-resources​

​文法後,上面的例子可改寫為:

try(FileInputStream fileInputStream1 = new FileInputStream("file.txt")){
    fileInputStream1.read();
} catch (IOException e) {
    e.printStackTrace();
}      

如何判讀資源是否真的被關閉了呢,我們手寫個Demo:

實作​

​AutoCloseable​

​接口的資源類。

class MyResource implements AutoCloseable{

    public void open(){
        System.out.println("resource is open!");
    }

    @Override
    public void close() throws Exception {
        System.out.println("resource is close!");
    }
}      

調用方:

public static void main(String[] args){

    try(MyResource myResource = new MyResource()){
        myResource.open();
    } catch (Exception e) {
        e.printStackTrace();
    }
}      

輸出如下,可以看到​

​close​

​方法被自動調用了。

resource is open!
resource is close!      

底層原理是什麼呢,看一下編譯後的​

​class​

​檔案:

try {
        MyResource myResource = new MyResource();
        Throwable var2 = null;

        try {
            myResource.open();
        } catch (Throwable var12) {
            var2 = var12;
            throw var12;
        } finally {
            if (myResource != null) {
                if (var2 != null) {
                    try {
                        myResource.close();
                    } catch (Throwable var11) {
                        var2.addSuppressed(var11);
                    }
                } else {
                    myResource.close();
                }
            }

        }
    } catch (Exception var14) {
        var14.printStackTrace();
    }
}      

很明顯,編譯器生成了​

​finally​

​​代碼塊,并在其中調用了​

​close​

​​ 方法,同JDK1.7之前的關閉資源操作的實作原理是相同的,但是可以看到,這裡多調用了一個​

​addSuppressed​

​​方法,這麼做其實是為了處理異常屏蔽,什麼是異常屏蔽,首先,我們先修改一下剛剛的Demo,使資源類在​

​open​

​​和​

​close​

​方法中抛出異常,并且使用JDK1.7之前的關閉資源的方法,資源類以及調用方代碼修改如下:

public void open() throws IOException {
    System.out.println("resource is open!");
    throw new IOException("open() exception!");
}

@Override
public void close() throws Exception {
    System.out.println("resource is close!");
    throw new IOException("close()  exception!");
}      
public static void main(String[] args) throws Exception {

    MyResource myResource = null;

    try{
        myResource = new MyResource();
        myResource.open();
    }finally {
        try {
            myResource.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}      

控制台列印如下:

使用try-with-resources優雅關閉資源

​open​

​​方法抛出的異常被自動忽略了,而異常資訊丢失将導緻程式調試困難,是以​

​try-with-resources​

​​文法中加入了​

​addSuppressed​

​​處理異常屏蔽,現在修改Demo為使用​

​try-with-resource​

​關閉資源,調用方代碼如下:

public static void main(String[] args) throws Exception {

    try(MyResource myResource = new MyResource()){
        myResource.open();
    }
}      

控制台列印如下圖:

使用try-with-resources優雅關閉資源

異常資訊中多了提示:​

​close​

​​方法中抛出的異常被​

​open​

​方法中抛出的異常抑制了。