推荐用枚举统一管理错误响应,因其天然适合表达有限、固定、带行为的错误状态,可封装code、msg及toErrorResult()方法,语义清晰、类型安全、支持格式化且修改不影响其他类编译。

直接用 public static final 定义错误响应常量是可行的,但容易导致语义混乱、维护困难、编译耦合强——真正稳妥的做法是把错误码和消息拆成结构化对象,并配合接口或枚举封装行为约束。
为什么不用接口定义错误常量
很多老项目会写一个 ErrorConst 接口,里面全是 public static final String XXX = "xxx"。这看似省事,实则埋雷:
- 接口本意是契约,不是配置容器;JVM 层面仍解析为
ErrorConst.CODE_NOT_FOUND,加不加前缀没区别 - 多个接口含同名常量(比如都叫
SYSTEM_ERROR),实现类一 implements 就编译报错 - 修改某个字符串值,所有引用该接口的类都要重新编译,哪怕它们根本不用这个常量
- 无法携带类型信息(比如 code 是
String还是int?msg 是否可国际化?)
推荐用枚举统一管理错误响应
枚举天然适合表达有限、固定、带行为的错误状态。每个枚举项可封装 code、msg,还能提供 toErrorResult() 方法生成响应对象:
public enum BizErrorCode {
SYSTEM_ERROR("C00001", "系统异常"),
PARAM_VALID_ERROR("S00004", "参数校验错误: %s"),
ID_NOT_FOUND("S00007", "根据ID没有查询到对象");
private final String code;
private final String msg;
BizErrorCode(String code, String msg) {
this.code = code;
this.msg = msg;
}
public ErrorResult toErrorResult(Object... args) {
String formattedMsg = args.length > 0 ? String.format(msg, args) : msg;
return new ErrorResult(code, formattedMsg);
}
}
调用时直接 BizErrorCode.PARAM_VALID_ERROR.toErrorResult("username"),语义清晰、类型安全、支持格式化,且修改枚举不影响其他类编译。
final 类 + 静态常量仍可用,但要控制粒度
如果团队坚持用 final 类,务必做到三点:
- 只放真正全局、极少变动的常量,比如 HTTP 状态码:
public static final int HTTP_200 = 200、HTTP_500 = 500 - 错误消息字符串不要硬编码在常量里,而是通过
MessageSource或 i18n 资源文件加载 - 避免把
ErrorResult实例本身声明为static final,因为其字段可能含动态内容(如时间戳、请求ID),应每次构建新实例
Spring Boot 下更推荐 @ConfigurationProperties
如果你的错误码需要外部化(比如从 application.yml 加载),@ConfigurationProperties 比 final 常量更灵活:
@ConfigurationProperties(prefix = "error.code")
@Data
public class ErrorCodeProperties {
private String systemError = "C00001";
private String paramValid = "S00004";
// 对应 yml 中 error.code.system-error: C00001
}
这样运维可热更新错误码映射,开发也不用改代码。但注意:它不替代枚举的语义约束力,适合配置型错误码,不适合业务逻辑强绑定的错误场景。
最易被忽略的一点:无论用枚举还是 final 类,code 字段必须全局唯一且有明确分类规则(比如 C 开头是系统级,S 开头是业务级),否则前端做错误分流时会踩坑。别让“看起来能跑”掩盖了规则缺失。

















