java 7 前需手动管理资源关闭:必须在 finally 中判空关闭,多个资源按逆序分层处理,关闭异常不得掩盖主异常,可封装 closequietly 工具方法。

在 Java 7 之前(即没有 try-with-resources 语法时),资源关闭必须手动管理,稍有疏忽就容易导致资源泄漏或异常掩盖。要严谨实现,核心是:**无论是否发生异常,都必须确保资源关闭;关闭过程中发生的异常不能掩盖原始异常;多个资源需按逆序关闭。**
用 finally 块保证关闭执行
这是最基础也最关键的保障。所有资源关闭逻辑必须放在 finally 中,而不是 try 或 catch 里。
-
错误写法:只在
try块末尾 close → 一旦前面抛异常,close 不会执行 -
正确写法:在
finally中判断非 null 后关闭
示例(FileInputStream):
InputStream is = null;
try {
is = new FileInputStream("data.txt");
// 读取操作
} catch (IOException e) {
// 处理读取异常
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// 记录关闭异常,但不抛出(避免掩盖主异常)
System.err.println("关闭输入流失败:" + e.getMessage());
}
}
}
多个资源需分层 try-finally 或嵌套 finally
当涉及多个资源(如输入流 + 输出流),不能简单堆在同一个 finally 里——后一个 close 抛异常可能中断前一个的关闭。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 推荐做法:为每个关键资源配独立的
try-finally块,嵌套或并列安排 - 关闭顺序应与打开顺序相反(LIFO),比如先开 input,再开 output,则先关 output,再关 input
示例(复制文件):
InputStream is = null;
OutputStream os = null;
try {
is = new FileInputStream("src.txt");
os = new FileOutputStream("dst.txt");
// 复制逻辑...
} finally {
if (os != null) {
try { os.close(); } catch (IOException ignored) {}
}
if (is != null) {
try { is.close(); } catch (IOException ignored) {}
}
}
避免关闭异常掩盖主异常
如果 try 块已抛出异常,而 finally 中的 close() 又抛异常,JVM 默认只抛出后者,前者丢失——这非常危险。
- 解决方案:在
finally的 close 异常处理中,不重新抛出,而是记录日志或使用addSuppressed()(Java 7+ 才有,老版本不可用) - 老版本只能选择吞掉关闭异常(用空 catch 或打印警告),前提是确认该异常不影响业务语义
- 切勿在 finally 中 throw 新异常或 re-throw 捕获的异常
封装工具方法减少重复代码
为降低出错概率,可提取通用关闭逻辑到静态工具方法中:
public class IoUtils {
public static void closeQuietly(Closeable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (IOException ignored) {
// 保持静默,或走统一日志框架
}
}
}
}
// 使用:
InputStream is = null;
try {
is = new FileInputStream("x.txt");
// ...
} finally {
IoUtils.closeQuietly(is);
}
这样既统一了行为,又让业务代码更清晰,也便于后续升级时集中替换为 try-with-resources。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










