Java高效编程(9):优先使用 try-with-resources 而非 try-finally

解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界

在Java编程中,许多资源(如 InputStreamOutputStreamjava.sql.Connection)必须在使用后手动关闭。然而,开发者常常忘记关闭这些资源,导致严重的性能问题。虽然终结器(详见【条目8】)曾经被用作安全网,但它们并不可靠。在 Java 7 之前,try-finally 语句是确保资源正确关闭的唯一方式,即使在异常或提前返回时,try-finally 也能保证资源关闭:

// 使用 try-finally 关闭资源 - 不再是最佳方式
static String firstLineOfFile(String path) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader(path));
    try {
        return br.readLine();
    } finally {
        br.close();
    }
}

虽然这种方式看起来并不坏,但在处理多个资源时,它会变得非常繁琐:

// 使用 try-finally 处理多个资源时代码显得复杂
static void copy(String src, String dst) throws IOException {
    InputStream in = new FileInputStream(src);
    try {
        OutputStream out = new FileOutputStream(dst);
        try {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0) {
                out.write(buf, 0, n);
            }
        } finally {
            out.close();
        }
    } finally {
        in.close();
    }
}

即使是优秀的程序员,也经常在 try-finally 中犯错。事实上,Java 库中有三分之二的 close 方法调用在2007年是错误的。

使用 try-finally 的问题

即使 try-finally 可以正确关闭资源,但它存在一个微妙的问题:try 块和 finally 块中的代码都可能抛出异常。例如,在 firstLineOfFile 方法中,readLine 方法可能由于设备故障抛出异常,而 close 方法也可能因此失败。在这种情况下,后续的异常会掩盖第一个异常,导致调试变得非常困难。虽然可以编写代码来抑制第二个异常,但几乎没有人会这么做,因为代码会变得冗长复杂。

Java 7 的解决方案:try-with-resources

Java 7 引入了 try-with-resources 语句,解决了 try-finally 的所有问题。要使用该语句,资源类必须实现 AutoCloseable 接口,该接口只有一个返回 voidclose 方法。Java 库和第三方库中的许多类现在都实现了该接口。以下是使用 try-with-resources 重写的 firstLineOfFile 方法:

// 使用 try-with-resources - 最佳资源关闭方式
static String firstLineOfFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

try-with-resources 同时处理多个资源时,代码更加简洁:

// 使用 try-with-resources 处理多个资源 - 简洁明了
static void copy(String src, String dst) throws IOException {
    try (InputStream in = new FileInputStream(src);
         OutputStream out = new FileOutputStream(dst)) {
        byte[] buf = new byte[BUFFER_SIZE];
        int n;
        while ((n = in.read(buf)) >= 0) {
            out.write(buf, 0, n);
        }
    }
}

try-with-resources 的优势

try-finally 相比,try-with-resources 更加简洁且易于维护。它不仅减少了冗余的代码,还提供了更好的异常处理机制。如果在 try 块和 close 方法中同时抛出异常,try-with-resources 会抑制后续异常,并保留最重要的第一个异常。抑制的异常不会被简单忽略,而是记录在异常堆栈中,并带有“抑制异常”的说明。你可以通过 Throwable.getSuppressed 方法程序化地访问这些抑制的异常。

你也可以像在普通 try-finally 中一样,为 try-with-resources 添加 catch 子句来处理异常。如下所示是一个不抛出异常的 firstLineOfFile 版本,它在无法打开文件时返回默认值:

// 带有 catch 子句的 try-with-resources
static String firstLineOfFile(String path, String defaultVal) {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    } catch (IOException e) {
        return defaultVal;
    }
}

通过这种方式,你可以优雅地处理异常,而无需增加代码的嵌套层次。

总结

try-with-resources 是关闭资源的最佳方式。它不仅代码简洁、清晰,而且能够正确处理多个异常,确保抛出的异常是最有用的,抑制的异常也不会丢失。与 try-finally 相比,try-with-resources 的优势明显,强烈建议在需要关闭资源的场景下使用这一语句。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值