在Java中如何自定义异常类_Java自定义异常实现思路

应继承Exception以强制调用方处理,适用于可预期需恢复的业务错误;继承RuntimeException用于程序bug等不可恢复错误,不强制处理。

继承 Exception 还是 RuntimeException?关键看是否强制处理

Java 自定义异常的核心判断点在于:你希望调用方**必须显式处理**这个异常,还是允许它“悄悄传播”。Exception 及其子类是检查型异常(checked),编译器强制要求 try-catchthrows;而 RuntimeException 是非检查型(unchecked),不强制处理。

  • 业务逻辑中可预期、需主动恢复的错误(如余额不足、参数格式非法),适合继承 Exception
  • 程序内部 bug 或不可恢复的错误(如空指针、数组越界),应继承 RuntimeException,避免污染正常流程
  • 不要为了“统一”而全用 RuntimeException——这会让调用方失去对关键错误的感知能力

构造方法怎么写?至少保留三个标准签名

自定义异常类应覆盖父类最常用的构造方法,否则使用者会遇到 new MyException("msg") 编译失败这类低级问题。标准做法是提供以下三个构造函数:

public class InsufficientBalanceException extends Exception {
    public InsufficientBalanceException() {
        super();
    }
public InsufficientBalanceException(String message) {
    super(message);
}

publi

c InsufficientBalanceException(String message, Throwable cause) { super(message, cause); }

}

  • 无参构造用于日志或框架自动抛出场景
  • String 构造是日常使用频率最高的,务必支持
  • Throwable 的构造用于链式异常封装(比如捕获 SQLException 后包装成业务异常)

要不要加字段和方法?只在真有必要时才扩展

多数情况下,仅靠继承 + 标准构造就足够了。只有当异常需要携带额外上下文且被上层明确消费时,才考虑加字段。例如:

  • 需要返回错误码供前端展示 → 加 private final int errorCode
  • 需要记录触发时的用户 ID 用于审计 → 加 private final String userId
  • 不要加 getter/setter 以外的业务方法 —— 异常不是工具类
  • 字段必须 final,保证异常对象不可变

常见误用:给异常加 log() 方法或 sendToMonitor() —— 这混淆了“描述错误”和“处理错误”的职责。

抛出和捕获时要注意什么?别让异常信息丢失

自定义异常的价值在于精准传达问题,但实际中常因用法不当而失效:

  • 抛出时不要只写 throw new MyException("error"),优先用带上下文的字符串:throw new MyException("转账失败:目标账户 " + accountId + " 不存在")
  • 捕获后不要吞掉异常:catch (MyException e) { /* 空 */ } —— 至少要 logger.error("", e)
  • 跨模块传递时,避免在 catch 块里 new 一个新异常却不传原始 cause:throw new ServiceException("调用下游失败", e) 才能保留堆栈
  • Spring 等框架可能对异常类型有特殊处理(如 @ResponseStatus),确保你的异常类满足其扫描条件(通常是 public、有默认构造)

真正难的不是写一个异常类,而是整个团队对每种异常的语义、处理边界和日志规范达成一致。没约定好“这个异常谁负责兜底”,再多的自定义也没用。