Java异常机制是责任分配系统,强制分类错误、传播路径和恢复策略;Throwable为根,Error表严重JVM问题不可捕获,Exception表可干预错误,分已检查(如IOException)与未检查(如NullPointerException);try-with-resources防资源泄漏;throw抛实例,throws声明可能异常;自定义异常需明确checked/unchecked并脱敏敏感信息。
Java异常机制不是“要不要捕获”的选择题,而是“谁该在什么时机、以什么方式响应错误”的责任分配系统。它强制把错误分类、传播路径和恢复策略写进类型签名和控制流里。
Java 要求所有异常对象都继承 Throwable,但它只允许两类子类参与正常流程:Error 和 Exception。两者的语义边界非常明确:
Error 表示 JVM 无法继续运行的严重问题,比如 OutOfMemoryError、StackOverflowError —— 这些不能也不该被业务代码 try-catchException 才是程序预期中可能出错、且调用方有能力干预的场景,比如 IOException、SQLException
Runt
imeException 及其子类(如 NullPointerException、ArrayIndexOutOfBoundsException)是“未检查异常”,编译器不强制处理;其余 Exception 子类(如 IOException)是“已检查异常”,方法签名必须声明 throws 或用 try-catch 包裹很多人以为 finally 总是最后执行,其实它会在 return 语句的值确定后、真正返回前插入执行。更关键的是:如果 try 或 catch 块里有 return,finally 仍会运行,甚至能修改返回值(对引用类型)或覆盖基本类型返回值(需谨慎)。
常见陷阱是手动 close 资源时忘记嵌套 try:
try {
InputStream is = new FileInputStream("a.txt");
// do something
is.close(); // 如果这里抛异常,close 就没执行
} catch (IOException e) {
// handle
}
正确做法是用 try-with-resources(JDK 7+),它自动调用 AutoCloseable.close():
try (InputStream is = new FileInputStream("a.txt")) {
// do something
} catch (IOException e) {
// handle
} // is.close() 在这里隐式调用,无论是否异常
throw 是动词,用于抛出一个具体的异常实例;throws 是修饰符,写在方法声明上,告诉调用方“我可能向上扔这些异常”。两者不能替代:
throw new IOException(),但声明里没写 throws IOException,编译直接报错(对已检查异常)throws IOException 却没在方法体里 throw,编译通过,但这是“谎报军情”,误导调用方做无谓的异常处理throws IOException, SQLException;子类异常可以被父类 catch,但 throws 列表不能省略子类(除非父类已声明)继承 Exception 得到已检查异常,继承 RuntimeException 得到未检查异常。选错会立刻影响 API 使用体验:
String message 和 Throwable cause 的构造函数,方便链式异常追踪最常被忽略的一点:异常消息里不要拼接敏感数据(如密码、token),堆栈里也别打印完整请求体 —— 生产环境日志脱敏不是可选项。