解锁Python编程的无限可能:《奇妙的Python》带你漫游代码世界
在Java编程中,许多资源(如 InputStream、OutputStream 和 java.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 接口,该接口只有一个返回 void 的 close 方法。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 的优势明显,强烈建议在需要关闭资源的场景下使用这一语句。




![[Linux]从零开始的网站搭建教程](https://i-blog.csdnimg.cn/direct/1cd47873981f4e9584d1c552f9cd82f0.png)













