Java 异常体系全解析
异常,不只是 try-catch
在 Java 世界里,异常(Exception)是程序运行时处理“非预期状态”的核心机制。如何定义错误边界?如何传递失败信息?如何保证系统在异常路径下依然健壮?
一、Java 异常体系全景图
Java 中所有“可抛出”的类型都继承自 java.lang.Throwable
,其派生出两个核心子类:
Throwable
├── Error // JVM 或系统级严重错误,程序通常无法恢复
└── Exception // 应用级异常,程序可捕获并处理├── RuntimeException // 运行时异常,编译器不强制处理└── 其他 Exception // 受检异常(Checked Exception),必须显式处理
Error
:留给 JVM,表示“系统崩了”,如OutOfMemoryError
、StackOverflowError
Exception
:留给开发者,表示“逻辑出错了”,如IOException
、SQLException
RuntimeException
:表示“编程错误”,如NullPointerException
、ArrayIndexOutOfBoundsException
二、Error:你不该捕获的“系统级灾难”
常见 Error 类型:
OutOfMemoryError
:堆/元空间/直接内存溢出StackOverflowError
:递归过深或栈帧过大NoClassDefFoundError
:类加载失败LinkageError
:类版本冲突、方法签名不一致
为什么不建议捕获 Error
例如捕获 OOM 后继续运行,可能导致数据错乱、连接泄漏、状态不一致 —— 比直接崩溃更危险!
三、Exception:受检 vs 非受检,设计的权衡
1. 受检异常(Checked Exception)
特点:编译器强制要求处理(try-catch 或 throws)
代表:IOException
、SQLException
、ClassNotFoundException
public void readFile(String path) throws IOException {FileReader reader = new FileReader(path); // 编译器强制你处理 IOException// ...
}
2. 非受检异常(Unchecked Exception / RuntimeException)
特点:编译器不强制处理,通常表示“编程错误”
代表:NullPointerException
、IllegalArgumentException
、IndexOutOfBoundsException
public void process(String input) {if (input == null) {throw new IllegalArgumentException(" "); // 主动抛出,语义清晰}// ...
}
四、try-catch-finally 执行机制深度解析
1. 基本执行顺序
public static int test() {int x = 1;try {System.out.println("try");return ++x; // 计算返回值(x=2),暂存} catch (Exception e) {System.out.println("catch");} finally {System.out.println("finally");x = 3; // 修改 x,但不影响已暂存的返回值}return x; // 这行不会执行
}
// 输出:
// try
// finally
// 返回值:2
核心机制:
return
表达式先计算,结果暂存finally
块无论如何都会执行(除System.exit()
或 JVM 崩溃)finally
中的return
会覆盖 try/catch 中的返回值
2. finally 中 return 的陷阱
public static int badExample() {try {return 1;} finally {return 2; // 覆盖了 try 的返回值,破坏逻辑,难以调试}
}
finally 只用于资源释放、状态清理、日志记录
绝不在 finally 中 return、throw、break、continue
五、Spring 中的全局异常处理:@ControllerAdvice
在 Web 应用中,我们通常在 Controller 层统一处理异常,避免每个方法写 try-catch。
1. 全局异常处理器示例
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public ResponseEntity<ErrorResponse> handleBusinessException(BusinessException e) {log.warn("业务异常: {}", e.getMessage(), e);return ResponseEntity.badRequest().body(new ErrorResponse(e.getCode(), e.getMessage()));}@ExceptionHandler(Exception.class)public ResponseEntity<ErrorResponse> handleUnexpectedException(Exception e) {log.error("系统异常", e);return ResponseEntity.status(500).body(new ErrorResponse("SYSTEM_ERROR", "系统繁忙,请稍后再试"));}
}
2. 错误响应体设计
public record ErrorResponse(String code, String message) {}
优势:
- 统一错误格式,前端可标准化处理
- 避免异常堆栈暴露给用户(安全)
- 日志集中记录,便于监控与排查