天天看点

使用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​

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