Java中的try-with-resource语句

Posted by Yuanyeex on Friday, September 29, 2017

try-with-resources是jdk7引入的新功能,它通过一个try语句,声明一个或着多个资源。这里的资源指的是实现了closeable或者AutoCloseable接口的类的实例。在try-with-resource中声明的资源,不管在整个过程中有没有抛出异常,都会把声明的资源释放掉。

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader bufferedReader 
         = new BufferedReader(new FileReader(new File(path)))) {
        return bufferedReader.readLine();
    }
}

在JDK7之前,这种资源的申请和释放通常是这样写:

static String readFirstLineFromFileTryFinally(String path) throws IOException {
    BufferedReader reader = null;
    try {
        reader = new BufferedReader(new FileReader(path));
        return reader.readLine();
    }
    finally {
        if (reader != null) {
            reader.close();
        }
    }
}

readFirstLineFromFileTryFinally中,我们需要自己在finally代码块中进行资源的释放操作。注意,这里声明资源(new BufferedReader(new FileReader(path))),使用资源(reader.readLine())和释放资源(reader.close())都会发生异常,如果try{}代码块中抛出了衣长,finally{}代码块释放资源的时候也发生异常,那么调用捕获的异常会是什么呢?答案是释放资源时产生的异常,这时候try代码块产生的异常被抑制了(Suppressed Exceptions)。

但是,如果我们使用try-with-resource这种方式,释放资源的异常会被抑制,你捕获的要么是你初始化资源产生的异常,要么是使用中产生的异常。

下面我们通过一个列子来验证在使用资源和释放资源都产生异常的情况下,try-with-resource会抛出使用的异常。首先创建一个资源类,该类实现了AutoCloseable接口,并定义了一个叫name()的API。

static class Resource implements AutoCloseable {
    public String name() throws IOException {
        throw new IOException("Exception from name().");
    }

    @Override
    public void close() throws Exception {
        System.out.println("Close resource and throw exception");
        throw new IOException("Exception from close().");
    }
}

这里,name()close()什么都不做,只是各自抛出一个异常。写一段程序来使用try-with-resource声明资源,并使用:

try (Resource resource = new Resource()) {
    resource.name();
}

运行的结果如下:

Close resource and throw exception
Exception in thread "main" java.io.IOException: Exception from name().
.... ... ...

可依看到,close() API也被调用了,但是打出来的异常却是来自name()方法的,说明try-with-resource机制帮我们抑制了来自释放资源的异常。

资源释放的顺序

在try-with-resource中,资源的释放顺序和资源的创建顺序是相反的。

catch和finally

try-with-resource和传统的try一样,都支持catch和finally代码块。但是,对于try-with-resource,所有的catch和finally代码块都会在你声明的资源被释放后运行。

举个例子,还是用上面定义的Resource类,执行下面的代码:

try (Resource resource = new Resource()) {
    resource.name();
}
catch (Exception e) {
    System.out.println("catch block: " + e.getMessage());
}
finally {
    System.out.println("finally block");
}

执行结果如下:

Close resource and throw exception
catch block: Exception from name().
finally block

可见catch代码块和finally代码块都是在资源被释放之后才执行的。

总结

Try-with-resource这种方式给我带来很多方便:

  1. 代码更简洁,不用手动去释放资源

  2. 支持声明多个资源

  3. 按照资源创建相反的顺序来释放资源

  4. 会优先抛出使用资源产生的异常,更方便定位问题

  5. 支持catch和finally代码块,他们会在资源释放之后执行。

「真诚赞赏,手留余香」

Yuanyeex

真诚赞赏,手留余香

使用微信扫描二维码完成支付