异常处理看似简单,实则暗藏玄机。异常影响性能吗?该捕获还是抛出?finally一定会执行吗?本文系统讲解Java异常处理。
一、异常体系
text
复制
下载
Throwable ├── Error(不可恢复) │ ├── OutOfMemoryError │ └── StackOverflowError └── Exception ├── RuntimeException(非受检) │ ├── NullPointerException │ └── IllegalArgumentException └── 受检异常 ├── IOException └── SQLException
二、受检 vs 非受检
受检异常:必须处理或声明,用于可恢复场景
非受检异常:编程错误,如NPE
java
复制
下载
// 受检异常 public void read() throws IOException { Files.readAllBytes(Path.of("file.txt")); } // 非受检 public void validate(String s) { if (s == null) throw new IllegalArgumentException("不能为null"); }三、异常的性能
创建异常需要填充栈轨迹,开销较大。
java
复制
下载
// 慢:用异常控制流程 try { while (true) { arr[index++] = getValue(); } } catch (ArrayIndexOutOfBoundsException e) { // 结束 } // 快:正常判断 while (index < arr.length) { arr[index++] = getValue(); }优化:重写fillInStackTrace避免填充栈。
java
复制
下载
public class LightException extends RuntimeException { @Override public synchronized Throwable fillInStackTrace() { return this; } }四、try-catch-finally
java
复制
下载
public int test() { try { return 1; } finally { return 2; // 覆盖try的return,不推荐 } } // 返回2finally不执行的情况:
System.exit()JVM崩溃
死循环
五、try-with-resources
java
复制
下载
// 旧方式 BufferedReader br = null; try { br = new BufferedReader(new FileReader("file.txt")); return br.readLine(); } finally { if (br != null) br.close(); } // 新方式 try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) { return br.readLine(); }自定义资源需实现AutoCloseable:
java
复制
下载
public class MyResource implements AutoCloseable { @Override public void close() { System.out.println("关闭资源"); } }六、异常链
保留原始异常信息:
java
复制
下载
try { // ... } catch (SQLException e) { throw new ServiceException("查询失败", e); }java
复制
下载
public class ServiceException extends RuntimeException { public ServiceException(String msg, Throwable cause) { super(msg, cause); } }七、异常处理反模式
7.1 吞掉异常
java
复制
下载
// 坏 try { doSomething(); } catch (Exception e) { // 什么都不做 } // 好 try { doSomething(); } catch (Exception e) { log.error("操作失败", e); throw new ServiceException("操作失败", e); }7.2 捕获过宽
java
复制
下载
// 坏 try { // ... } catch (Exception e) {} // 好 try { // ... } catch (IOException e) { // 处理I/O异常 } catch (SQLException e) { // 处理数据库异常 }7.3 用异常控制流程
java
复制
下载
// 坏 try { return map.get(key).getValue(); } catch (NullPointerException e) { return defaultValue; } // 好 Value v = map.get(key); return v != null ? v.getValue() : defaultValue;八、全局异常处理
Spring Boot中:
java
复制
下载
@RestControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BusinessException.class) public Result<Void> handleBusiness(BusinessException e) { return Result.error(e.getCode(), e.getMessage()); } @ExceptionHandler(MethodArgumentNotValidException.class) public Result<Void> handleValidation(MethodArgumentNotValidException e) { String msg = e.getBindingResult().getFieldErrors().stream() .map(FieldError::getDefaultMessage) .collect(Collectors.joining(", ")); return Result.error(400, msg); } @ExceptionHandler(Exception.class) public Result<Void> handleUnknown(Exception e) { log.error("未知异常", e); return Result.error(500, "系统繁忙"); } }九、最佳实践
不要吞异常,至少记录日志
捕获具体异常,不要catch Exception
用异常链保留原因
不要用异常控制流程
受检异常用于可恢复场景
资源用try-with-resources
自定义异常继承RuntimeException
异常信息要具体,包含关键参数
十、总结
异常处理的核心是"不吞、不滥用、保留上下文"。合理的异常设计能提升系统可维护性和可观测性。