filenotfoundexception总被ioexception“吃掉”是因为它是ioexception的子类,catch顺序必须由具体到宽泛,否则编译报错;需用try-with-resources防资源泄漏;路径不存在常因父目录未创建,应主动mkdirs();exists()有竞态风险,优先try-catch。

FileNotFoundException 为什么总被 IOException “吃掉”?
因为 FileNotFoundException 是 IOException 的子类,Java 的 catch 块按顺序匹配——如果先写 catch (IOException e),后面的 catch (FileNotFoundException e) 就永远进不去,编译器甚至会报错“已捕获的异常”。这不是 bug,是继承关系决定的语义逻辑。
- 必须把更具体的异常放在前面:
FileNotFoundException→IOException(其他 IO 错误)→ 可选的SecurityException等 - 如果只 catch
IOException,你无法区分“文件不存在”和“磁盘满”“权限不足”等不同场景,后续重试、提示或日志都失去针对性 - IDE(如 IntelliJ)通常会自动警告并帮你调整 catch 顺序,别点“忽略”
用 try-with-resources 不只是省代码,更是防泄漏
手动调用 fis.close() 或 fos.close() 很容易漏写、写在错误位置,或者在异常发生后跳过关闭逻辑,导致句柄堆积、文件被锁死。而 try-with-resources 在 Java 7+ 中强制要求资源实现 AutoCloseable,无论是否抛异常,都会在块结束时自动调用 close()。
- 资源声明必须在
try (…)括号内,且只能是支持自动关闭的类型(FileInputStream、BufferedReader、FileOutputStream等都符合) - 多个资源用分号隔开:
try (FileInputStream fis = new FileInputStream("a.txt"); FileOutputStream fos = new FileOutputStream("b.txt")) { … } - 不要在 try 块里再手动
close(),否则可能触发IOException(流已关闭),反而干扰主逻辑
“文件不存在”不等于路径写错了——常见误判点
看到 java.io.FileNotFoundException: d:studyioinfo.txt (系统找不到指定的路径。),很多人第一反应是“文件名拼错了”,但更常踩的坑其实是:父目录压根就不存在。
-
FileOutputStream能自动创建**文件**,但不会创建**中间目录**。路径d:/study/io/info.txt中若d:/study/io不存在,就会直接抛FileNotFoundException - 用
new File(path).getParentFile().mkdirs()主动创建目录树,再执行 IO 操作 - 相对路径易受
user.dir影响,运行前加一句System.out.println(System.getProperty("user.dir"))确认当前工作目录 - Windows 下路径分隔符用
/或\都行,但硬写会触发转义(如"d:studyio"报错),务必写成"d:\study\io"或"d:/study/io"
什么时候该检查 exists(),什么时候该直接 try-catch?
提前用 file.exists() 判断看似稳妥,实则引入竞态条件(TOCTOU):判断时存在,真正 open 时已被删除或权限变更。除非业务强依赖“存在即可用”的原子性(如配置文件预检),否则应优先信任 try-catch。
- 读操作:直接 try-catch
FileNotFoundException更可靠;exists()只适合做调试输出或用户友好提示(如“配置文件未找到,请检查config.json是否存在”) - 写操作:若需确保目录可写,可用
file.getParentFile().canWrite()辅助判断,但最终仍要靠 catch 处理实际失败 - 注意:
exists()对符号链接、NFS 挂载点等特殊文件系统可能返回不准,而异常机制始终真实反映 OS 层面的打开结果
最麻烦的不是异常本身,而是把 FileNotFoundException 当作普通错误处理,却忽略了它背后暴露的路径构造逻辑、目录初始化缺失或部署环境差异——这些往往藏在构建脚本、Dockerfile 或 CI 配置里,不在 Java 代码中。






