应使用具体异常类型替代exception,如ioexception、illegalargumentexception或自定义runtimeexception子类,避免throws exception;评估是否需声明检查异常,优先用语义化unchecked异常;杜绝空catch和模糊包装,让异常成为清晰的接口契约。

直接抛出 Exception 或 Throwable 这类宽泛异常类型,SonarQube 会标记为 “Exception is broad”,因为它违反了异常处理的最佳实践:应明确区分可恢复的检查异常(checked)、不可恢复的运行时异常(unchecked),并用具体类型表达错误语义。
用更具体的异常类型替代 Exception
不要声明 throws Exception,而是根据方法实际可能发生的错误场景,选择或定义语义清晰的异常:
- IO 操作失败 → 改用
IOException - 文件不存在 → 可抛
FileNotFoundException(是IOException子类) - 参数非法 → 抛
IllegalArgumentException(无需声明,属于 unchecked) - 业务规则不满足(如余额不足)→ 自定义
InsufficientBalanceException(通常继承RuntimeException)
检查是否真需要声明检查异常
并非所有异常都该向上抛。评估调用方能否合理处理该异常:
- 如果上层无法重试、补偿或给出用户友好提示,就不要抛 checked 异常,改用合适的 unchecked 异常包装后抛出(如
new RuntimeException("DB timeout", e)) - 避免为了“满足编译”而盲目加
throws Exception;宁可捕获后转为业务可理解的异常 - 工具类方法(如 JSON 解析)若只可能因输入错误失败,可直接抛
IllegalArgumentException或自定义 unchecked 异常,不声明 throws
必要时定义语义化的自定义异常
当 JDK 异常无法准确表达业务含义时,创建轻量级自定义异常:
- 继承
RuntimeException(推荐用于大多数业务异常,不强制调用方处理) - 类名体现场景,如
PaymentValidationException、UserLockedException - 提供带消息和原因的构造函数:
super(message, cause) - 不需要序列化?可加
transient字段或忽略serialVersionUID
清理无意义的异常包装或吞没
SonarQube 提示 broad 异常,有时源于防御性但模糊的异常处理:
- 避免
catch (Exception e) { throw new RuntimeException(e); }—— 丢失原始类型信息 - 不要空 catch(
catch (Exception e) {}),这比 broad throws 更危险 - 若必须兜底,至少记录日志并保留原始异常类型:
log.error("Unexpected error", e); throw e;
重构核心是让异常成为接口契约的一部分:调用方一看 throws IOException, UserNotFoundException 就知道要处理什么,而不是面对一个笼统的 Exception 束手无策。不复杂但容易忽略。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











