Java中未在finally中关闭Stream流的核心问题是“关不牢”,异常发生时流无法释放导致句柄泄漏;必须关闭绑定系统资源的流(如FileInputStream、Socket流等),而内存流(如ByteArrayInputStream)无需手动关闭;推荐使用try-with-resources确保自动、逆序、可靠关闭。

Java中未在finally中关闭Stream流,核心问题不是“没关”,而是“关不牢”——异常一来,流就悬在那儿,句柄持续占用,直到进程重启或系统崩溃。
为什么finally里不关就容易出事
很多代码只在try块末尾调用close(),但一旦read()或write()抛出IOException,后续语句直接跳过,流对象根本没机会释放。更隐蔽的是:即使写了finally,若没做null检查,流初始化失败时调用close()会触发NullPointerException,反而掩盖原始异常。
哪些流必须关、哪些其实不用
不是所有流都需手动关闭:
- 必须关闭的:FileInputStream、FileOutputStream、BufferedReader、BufferedWriter、ObjectInputStream、ObjectOutputStream、Socket.getInputStream()等——它们背后绑定了操作系统文件描述符或网络连接
- 通常不用关的:ByteArrayInputStream、ByteArrayOutputStream、StringReader、StringWriter——数据在内存中,不涉及底层资源
真正可靠的关闭写法
别再手写finally了。JDK 7+ 的try-with-resources是事实标准,它会在退出作用域时自动调用close(),且能按声明逆序关闭嵌套流(比如BufferedReader → FileReader → FileInputStream),避免顺序错误。
立即学习“Java免费学习笔记(深入)”;
示例:
```javatry (FileInputStream fis = new FileInputStream("a.txt");
BufferedInputStream bis = new BufferedInputStream(fis);
DataInputStream dis = new DataInputStream(bis)) {
// 读取逻辑
} // 所有流在此自动关闭,无论是否异常
嵌套和包装流的关闭陷阱
常见误区是只关外层、漏掉底层源头。例如:
- 用new BufferedReader(new InputStreamReader(conn.getInputStream())),只关BufferedReader不够,conn.getInputStream()本身也得由你负责——除非你确认上层框架已托管其生命周期
- Spring RestTemplate或OkHttp返回的Response.body().byteStream(),必须显式消费并关闭,否则socket句柄卡在CLOSE_WAIT状态
- XML/JSON解析器(如DocumentBuilder.parse(is))绝不会帮你关传入的InputStream,必须自己包在try-with-resources里


















