自定义异常类必须继承Exception或RuntimeException,提供三个标准构造函数,不重写getMessage()和getCause(),命名体现业务语义而非技术细节,并配合@ControllerAdvice统一处理。

自定义异常类必须继承 Exception 或 RuntimeException
Java 中抛出自定义异常的前提是它得是 Throwable 的子类。绝大多数业务异常应继承 Exception(编译期检查异常),强制调用方处理;若属于程序逻辑错误(如参数非法、状态不一致),可继承 RuntimeException(运行时异常),避免处处 try-catch 或声明 throws。
常见错误是直接继承 Throwable 或空参构造不调用父类构造,导致堆栈信息丢失或序列化失败。
- 继承
Exception:适合需显式处理的业务场景,如支付超时、库存不足 - 继承
RuntimeException:适合开发阶段应避免发生的错误,如IllegalArgumentException的语义延伸 - 务必提供至少三个标准构造函数:
MyException()、MyException(String message)、MyException(String message, Throwable cause)
getMessage() 和 getCause() 不要被覆盖成空逻辑
自定义异常类中,除非有明确封装需求(如脱敏敏感字段),否则不要重写 getMessage() 或 getCause()。JVM 日志、IDE 调试、监控系统都依赖这些方法返回原始信息。
例如,以下写法会掩盖真实错误原因:
立即学习“Java免费学习笔记(深入)”;
public class PaymentException extends Exception {
public PaymentException(String message) {
super(message);
}
@Override
public String getMessage() {
return "系统繁忙,请稍后再试"; // ❌ 隐藏原始 message,调试困难
}
}
正确做法是保留原始信息,必要时在日志中补充上下文:
- 用
super(message, cause)传递原始异常链 - 在
catch块中用log.error("支付失败,订单ID: {}", orderId, e)补充业务字段 - 避免在异常类内部做字符串拼接或格式化,交给日志层或调用方
异常类名要体现“什么错了”,而不是“怎么错的”
命名反映语义层级,而非技术路径。比如 InsufficientBalanceException 比 AccountBalanceCheckFailedException 更清晰;OrderAlreadyShippedException 比 UpdateOrderStatusException 更准确。
使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888
容易踩的坑:
- 带动词或方法名(如
ValidateUserException)—— 这是动作,不是状态 - 含技术实现细节(如
JDBCConnectionTimeoutException)—— 应抽象为业务语义,如DatabaseUnavailableException - 用缩写或模糊词(如
InvldParamEx或BusinessError)—— 无法快速定位问题域
Spring 项目中配合 @ControllerAdvice 统一处理更实用
单独定义异常类意义有限,关键在于如何与框架协同。Spring MVC 中,自定义异常配合全局异常处理器能统一响应格式、HTTP 状态码和日志记录。
典型结构:
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(InsufficientStockException.class)
@ResponseStatus(HttpStatus.PRECONDITION_FAILED)
@ResponseBody
public ApiResponse handleInsufficientStock(InsufficientStockException e) {
return ApiResponse.fail("库存不足", "STOCK_SHORTAGE");
}
}
注意点:
- 确保异常类在
@ExceptionHandler方法签名中精确匹配(不建议捕获Exception后再 instanceof 判断) - 避免在
@ExceptionHandler中抛出新异常,否则可能绕过统一处理逻辑 - 如果使用 Spring Boot +
ResponseEntityExceptionHandler,优先复用其默认行为,再针对性扩展
真正难的不是写一个异常类,而是让整个团队对每个异常的触发条件、恢复方式和日志埋点达成一致。名字、构造方式、是否检查、在哪捕获——这些决策一旦分散,就会变成排查时的噪音源。









