Java处理IO异常的核心是区分可恢复与不可恢复场景:可恢复时重试或降级,不可恢复时抛出;必须精准捕获子类异常而非笼统catch IOException,优先声明throws交由上层决策,配合try-with-resources确保资源关闭并检查suppressed异常。

Java里处理IO异常,核心不是“捕获所有异常”,而是区分可恢复场景与不可恢复场景,再决定是重试、降级、还是抛出。
什么时候该用 try-catch 包住 IOException
仅当你能对具体失败做出有意义响应时才捕获——比如文件临时不可读,你打算等1秒后重试;或网络流断开,你切换备用地址。盲目 catch (IOException e) { e.printStackTrace(); } 会掩盖问题,且无法继续执行后续逻辑。
- 适合捕获的场景:
FileInputStream打开失败但路径可修正;Socket连接超时后尝试重连 - 不适合捕获的场景:读取配置文件失败却静默跳过,导致后续空指针;写日志时
IOException被吞掉,丢失关键错误上下文 - 注意:
IOException是检查异常(checked),编译器强制你处理,但不等于必须在当前方法里catch——更合理的是声明throws IOException,交由上层统一决策
try-with-resources 必须用,且不能替代异常处理逻辑
它只保证资源关闭,不处理业务异常。如果你在 try 块里读取文件出错,close() 仍会触发,但那个 IOException 可能被压制(suppressed),尤其当 close() 自己也抛异常时。
try (FileInputStream fis = new FileInputStream("config.txt")) {
int b = fis.read(); // 这里抛 IOException
// ... 处理逻辑
} catch (IOException e) {
// 注意:e 可能已包含被压制的 close 异常,用 e.getSuppressed() 查看
}
- 永远优先用
try-with-resources,避免手动finally关闭出错 - 不要以为加了
try-with-resources就算处理完 IO 异常——它不管读写过程中的失败 - 若需记录关闭失败,应在
catch块里检查e.getSuppressed()
区分 IOException 子类做精细化响应
不同子类代表不同语义:比如 FileNotFoundException 说明路径不存在,可以创建目录再试;SocketTimeoutException 属于网络抖动,适合重试;而 EOFException 在反序列化中常表示数据截断,应视为严重错误。
立即学习“Java免费学习笔记(深入)”;
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("data.ser"));
MyData data = (MyData) ois.readObject();
} catch (FileNotFoundException e) {
// 可以主动创建默认文件,或提示用户导入
} catch (SocketTimeoutException e) {
// 重试,最多3次
} catch (EOFException e) {
// 数据损坏,记录告警并拒绝加载
} catch (IOException e) {
// 兜底:其他未预期 IO 问题,原样抛出或转为运行时异常
throw new UncheckedIOException(e);
}
- 不要只捕获父类
IOException,忽略子类差异 -
UncheckedIOException是 JDK 7+ 提供的包装类,能把检查异常转为运行时异常,适合不想层层throws的场景 - 自定义业务异常时,建议用
IOException作 cause,保留原始堆栈
最常被忽略的一点:IO异常往往暴露的是环境问题——磁盘满、权限不足、NFS挂载失效。捕获后光打日志没用,得结合监控(如磁盘使用率、inode剩余数)和告警,才能真正防住。








