必知的8个异常处理误区

引言

在Java开发中,异常处理看似简单,却隐藏着无数“暗坑”。一个未被妥善处理的异常可能导致服务雪崩、数据不一致,甚至引发安全漏洞。本文深入分析新手常见的8个异常处理误区,并提供实战解决方案,助你写出更健壮的代码。

捕获Throwable或Error

错误示例

try {
  // 业务代码
} catch (Throwable e) { // 包括 Error
  e.printStackTrace();
}

问题

  • Error 表示 JVM 无法处理的严重问题(如,OutOfMemoryError),捕获后可能导致程序无法正常终止;
  • 可能掩盖真正需要关注的业务异常。

正确做法

try {
  // 业务代码
} catch (SpecificException e) { // 明确捕获具体异常
  handleException(e);
} catch (Exception e) { // 通用异常兜底
  log.error("Unexpected error", e);
}

忽略异常(空 catch 块)

错误示例

try {
  FileInputStream fis = new FileInputStream("data.txt");
} catch (FileNotFoundException e) {
  // 空 catch 块,假装问题不存在
}

后果

  • 文件示找到时程序静默失败,后续操作可能引发更隐蔽的异常;
  • 难以定位根原因,增加调试成本。

改进方案

try {
  FileInputStream fis = new FileInputStream("data.txt");
} catch (FileNotFoundException e) {
  log.error("文件不存在", e);
  throw new BusinessException("数据文件缺失,请检查配置", e); // 封装并传递
}

过度捕获异常

错误示例

try {
  userService.save(user);
  orderService.createOrder(user);
} catch (Exception e) { // 捕获所有异常
  rollbackTransaction();
}

问题

  • 可能意外捕获 RuntimeException(如,NullPointerException),掩盖代码逻辑错误;
  • 无法针对不同异常实施差异化处理。

优化策略

try {
  userService.save(user);
  orderService.createOrder(user);
} catch (OptimisticLockingFailureException e) {
  retryTransaction(); // 乐观锁冲突重试
  handleConstraintViolation(e); // 处理数据约束问题
  log.error("未知异常", e);
  rollbackTransaction();
}

finally 块中使用 return

错误示例

public int calculate() {
  try {
    return 1 / 0; // 抛出 ArithmeticException
  } finally {
    return 0;
  }
}

后果

  • 掩盖原始异常,返回错误数据;
  • 调试时无法发现真实的异常堆栈。

正确写法

public int calculate() throws ArithmeticException {
  try {
    return 1 / 0;
  } finally {
    cleanupResources(); // 仅释放资源,不返回值
  }
}

滥用异常控制流程

错误示例

try {
  User user = userRepository.findById(id);
} catch () {
  createNewUser(id);
}

问题

  • 异常处理成本远高于正常判断逻辑;
  • 破坏代码可读性。

改进方案

Optional<User> userOpt = userRepository.findById(id);
if (userOpt.isPresent()) {
  return userOpt.get();
} else {
  return createNewUser(id); // 正常流程处理
}

不关闭资源的 try-with-resources

错误示例

BufferReader br = new BufferReader(new FileReader("data.txt"));
try {
  String line = br.readLine();
  //...
} finally {
  if (br != null) {
    br.close(); // 手动关闭易遗漏
  }
}

隐患

  • 资源未关闭可能导致内存泄漏或文件句柄耗尽。

正确做法

try (BufferReader br = new BufferReader(new FileReader("data.txt"))) {
  String line = br.readLine();
  //...
} // 自动关闭

忽略异常链(丢失根因)

错误示例

try {
  parseData(json);
} catch (JsonParseException e) {
  throw new BusinessException("数据解析失败"); // 丢失原始异常
}

后果

  • 日志中无法追踪原始异常堆栈,增加排查难度。

改进方案

try {
  parseData(json);
} catch (JsonParseException e) {
  throw new BusinessException("数据解析失败", e); // 传递原始异常
}

过度自定义异常类型

错误示例

// 自定义几十种异常类型,但处理方式相同
throw new UserNotFoundInCacheException();
throw new UserNotExistInDBException();

问题

  • 增加维护成本,降低代码可读性;
  • 可能导致重复处理逻辑。

优化建议

// 按处理方式分类异常
throw new NotFoundException("用户不存在", EntityType.USER);
throw new NotFoundException("订单不存在", EntityType.ORDER);
posted @ 2025-07-24 09:38  凉皮也是菜  阅读(21)  评论(0)    收藏  举报