【知识点小结】try-with-resources 语句

介绍Java7中引入的try-with-resources语句,这是一种用于自动关闭资源的语法糖,提高了代码的健壮性和可读性。文章对比了传统try-finally语句的不足,解释了TWR如何简化资源管理,并提供了示例代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

try-with-resources (下简称“TWR”)是 Java 7 的新特性,是一种特殊的 try 语句,特殊性主要表现在两个方面:与原来的 try 语句在语法上有少许差异;其中定义了资源并会自动关闭它们。所谓“资源”,就是诸如流等需要关闭的对象。
《The JavaTM Tutorials》1相关示例如下:

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

不堪回首的过去

为什么要加入这种特性?之前是怎么样的呢?
Java 7 之前,通常使用 try-finally 语句并在 finally 中关闭资源。
虽然 finally 块被认为是关闭资源的最佳之处,但想要写出一段“完美”的资源使用代码被认为是困难的——甚至官方的示例也难免有错。
而且,如果涉及多个资源的话,资源关闭代码将变得冗长且更易出错。
总之,Java 7 之前,如果没有写出“完美”的资源使用代码,而程序仍正常运行通常只是因为异常概率极小。
基于资源的关闭操作通常都是模版化的,因此,引入 TWR 这样的语法糖就成为可能。

进一步说明

从上文的示例可以看到,与普通的 try 语句相比,TWR 的 try 关键字与代码块间多了一对圆括号,其中是要使用的资源对象的声明。要注意的是,其中声明的资源类必须是 java.lang.AutoCloseable 的实现类,否则编译错误。

要确保 TWR 生效,正确的用法是为各个资源声明独立变量。

TWR 中资源是按声明顺序相反的顺序关闭的。

TWR 语句也可以有 catch 和 finally 块,它们在声明的资源被关闭后运行。不过通常很少会这么用。

比一比

AutoCloseable 接口

TWR 中声明的资源类应实现 AutoCloseable 接口,这是 Java 7 新增的接口,它被实现为 Closeable 接口的父接口。因此,Java 7 中所有的资源类几乎都实现了 AutoCloseable 接口。
根据方法重写的限制,AutoCloseable.close() 抛出 Exception,而 Closeable.close() 抛出 IOException。

异常的抑制

传统的 try-finally 写法,try 块(资源声明及使用)和 finally 块(关闭资源)都可能抛出异常,如果都抛出异常,则 try 块中的异常将被抑制(suppressed),只能获取 finally 块中的异常信息。

对于 TWR 而言,也会有类似的情况,但是 try 块中的异常会被重抛,关闭异常会被抑制。被抑制的异常会通过 Throwable.addSuppressed() 方法增加到 try 块异常上,随后可通过调用 Throwable.getSuppressed() 方法获取被抑制的异常。(注意,这两个方法也是 Java 7 新增的。)

我们可以写一个示例来查看两种语句抛出的异常情况:

import org.testng.annotations.Test;

import java.io.IOException;
import java.io.InputStream;

public class TryResourceTest {

    @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "close error")
    public void testOrdinaryTry () throws IOException {
        InputStream is = null;
        try {
            is = new MyInputStream();
            is.read();
        } finally {
            is.close(); // 最终抛出关闭异常
        }
    }

    @Test(expectedExceptions = IOException.class, expectedExceptionsMessageRegExp = "read error")
    public void testTwr() throws IOException {
        try (InputStream is = new MyInputStream()) {
            is.read(); // 读取异常会被重新抛出
        }
    }

    class MyInputStream extends InputStream {

        @Override
        public int read() throws IOException {
            throw new IOException("read error");
        }

        @Override
        public void close() throws IOException {
            throw new IOException("close error");
        }
    }
}

附录

一种原始的编程技巧

普通 try 语句使用资源时,有一种常用的编程技巧,称为“双层捕获关闭流”,代码类似下面这样:

InputStream in = null;
try {
    try {
      // code that might throw exception
    } finally {
      in.close();
    }
} catch(IOException e) {
    // show error message
}

这样写的好处是使得 try 语句符合了单一职责原则。内层只确保关闭流,外层确保报告异常。

参考

The JavaTM Tutorials - The try-with-resources Statement
Effective Java 第三版——9. 使用try-with-resources语句替代try-finally语句
JDK1.8中的try-with-resources声明


  1. The JavaTM Tutorials - The try-with-resources Statement ↩︎

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值