IOException必须在最靠近IO操作的位置捕获或声明,如FileInputStream、BufferedReader、Files.readAllLines()调用点;Files工具类建议直接throws,手动流需用try-with-resources;Scanner不抛IOException;AutoCloseable是关键前提;Files异常语义精细,应前置校验而非硬判子类;日志须保留原始异常并打印suppressed异常。

Java里IOException到底该在哪儿捕获
别一上来就套try-catch包住整个方法——很多同学把IOException全扔给main或者上层统一处理,结果文件没关、资源泄漏、错误堆栈还看不出哪一行出的问题。
真实场景里,IOException必须在**最靠近IO操作的位置捕获或声明**,尤其是涉及FileInputStream、BufferedReader、Files.readAllLines()这些具体调用点。否则你根本不知道是打开失败、读取中途断了,还是写入时磁盘满了。
- 用
Files工具类(如Files.readAllBytes())时,直接声明throws IOException更干净,别硬吞 - 手动管理流(
FileInputStream等)必须配try-with-resources,否则close()失败也会抛新的IOException,和原异常搅在一起 -
Scanner读文件时不抛IOException,它把IO错误转成InputMismatchException或静默跳过——这点容易误判
try-with-resources不是万能的,漏掉这三类资源会崩
try-with-resources只对实现了AutoCloseable的类生效,但很多“看起来该关”的东西其实不在此列,比如Socket的getInputStream()返回的流,或者你自己包装的BufferedWriter子类忘了重写close()逻辑。
常见翻车点:
立即学习“Java免费学习笔记(深入)”;
-
new FileInputStream(new File("x.txt"))——FileInputStream实现了AutoCloseable,没问题 -
new ZipInputStream(new FileInputStream("a.zip"))——ZipInputStream也实现了,OK -
new BufferedReader(new InputStreamReader(new FileInputStream("x.txt")))—— 三层嵌套,只要外层BufferedReader在try括号里,所有内层都会被自动关闭 -
new FileOutputStream("out.txt", true)(追加模式)—— 如果写到一半抛IOException,close()可能失败,但异常会被压制(suppressed),得用Throwable.getSuppressed()手动捞
用Files API时,IOException的语义比你想象的细
Files系列方法看似简单,但每个抛出的IOException背后对应不同系统级错误:权限不足、路径不存在、设备忙、符号链接循环……光靠e.getMessage()很难区分。
实操建议:
- 用
Files.exists(path, LinkOption.NOFOLLOW_LINKS)提前检查,别等readAllLines()才爆NoSuchFileException(它是IOException子类) -
Files.copy(in, out, StandardCopyOption.REPLACE_EXISTING)在Windows下可能因文件被占用抛AccessDeniedException(也是IOException子类),Linux下可能是FileSystemException - 不要用
instanceof硬判子类名,改用Files.isReadable()/Files.isWritable()做前置校验,更稳定
日志里只打e.toString()等于没打
很多人在catch块里只写log.error("read failed", e),但生产环境看不到完整堆栈,也没法判断是网络存储超时还是本地磁盘坏道。
关键动作:
- 必须保留原始异常对象传给日志框架,别
e.getMessage()后拼字符串 - 对
Files操作,加一行log.debug("trying to read: {}", path.toAbsolutePath()),路径绝对化能避开相对路径歧义 - 如果用了
try-with-resources且有suppressed异常,记得在日志里显式打印:for (Throwable s : e.getSuppressed()) { log.warn("suppressed", s); }
IO异常从来不是单点问题,它连着路径、权限、挂载状态、JVM文件句柄限制。多打一行诊断日志,比事后翻监控快十倍。










