只有继承自Exception但不是RuntimeException子类的异常(如IOException、SQLException)必须用try-catch处理;RuntimeException及其子类(如NullPointerException)无需强制处理;自定义异常若继承Exception则强制处理,继承RuntimeException则不强制。

Java里哪些异常必须用try-catch处理
只有继承自Exception但**不是**RuntimeException子类的异常,编译器才会强制你捕获或声明。比如IOException、SQLException、ParseException——不处理,代码直接编译失败。
常见误判:以为NullPointerException或ArrayIndexOutOfBoundsException也要强制处理,其实它们是RuntimeException子类,属于unchecked,编译器不管。
-
FileNotFoundException→ 必须处理(父类是IOException) -
IllegalArgumentException→ 不用处理(RuntimeException子类) - 自定义异常若继承
Exception→ 默认强制处理;若继承RuntimeException→ 不强制
try-catch写法中容易被忽略的资源泄漏问题
单纯套个try-catch不等于安全。比如打开文件后只在try块里读,没关流,即使捕获了IOException,finally里又没显式close(),就可能泄露句柄。
更稳妥的做法是用try-with-resources(Java 7+),它自动调用close(),哪怕抛异常也保证执行:
立即学习“Java免费学习笔记(深入)”;
try (FileInputStream fis = new FileInputStream("data.txt")) {
int b = fis.read();
} catch (IOException e) {
// 编译器要求必须处理这个checked异常
}
- 资源类必须实现
AutoCloseable接口(FileInputStream、BufferedReader等都实现了) - 不要在try-with-resources括号里写赋值语句以外的逻辑,否则编译报错
- 如果多个资源,用分号隔开:
try (A a = new A(); B b = new B()) { ... }
catch多个Checked异常时的顺序和合并写法
多个catch块必须按“子类在前、父类在后”,否则编译报错exception XXX has already been caught。比如不能把Exception写在IOException前面。
Java 7起支持多异常捕获语法,减少重复代码:
try {
doSomething();
} catch (IOException | SQLException e) {
logger.error("I/O or DB error", e);
}
- 多个异常类型用
|分隔,它们之间不能有继承关系(比如不能写IOException | Exception) - 捕获变量
e的静态类型是这些异常的最近公共父类(上例中是Exception) - 如果需要针对不同异常做不同处理,还是得拆成独立
catch块
throws声明比try-catch更合适的情况
不是所有Checked异常都该当场捕获。如果你的方法本身不负责恢复错误(比如工具类里的readConfig(String path)),往上抛更合理——让调用方决定怎么处理。
声明方式:public String readConfig(String path) throws IOException。这时调用方要么try-catch,要么继续throws。
- 避免在每个层级都
catch再throw new RuntimeException(e)——这等于绕过Checked机制,丢失原始异常类型信息 - 如果方法签名已声明
throws IOException,内部调用Files.readAllLines()就不需要再包一层try-catch - 注意:重写父类方法时,子类
throws的异常不能比父类更宽(即只能是父类声明异常的子集或不抛)
Checked异常的设计本意不是让你到处补try-catch,而是迫使你在关键错误点做明确决策:这里能恢复吗?不能就交给上层。最容易被忽略的是——把本该传播的异常硬塞进空catch块里,或者只打一行日志就吞掉,结果出问题时完全找不到源头。








