优先用enum表达固定互斥取值,因其编译期防错、ide补全、线程安全、不可变且反射受限;避免滥用public static final string或在enum中添加可变状态、耗时操作及复杂字段。

Java里用enum定义常量比public static final更安全
直接说结论:想表达一组固定、互斥的取值(比如状态、协议类型、颜色),优先用enum,不是因为“高级”,而是它天然防错——编译器能拦住非法赋值,IDE能自动补全,序列化/反序列化也更稳。
常见错误是把enum当普通类写,加一堆无意义的构造函数或字段;或者反过来,该用enum的地方硬写public static final String,结果运行时拼错字符串就崩。
-
enum实例在类加载时就初始化完毕,不可变、线程安全,不用额外加final或synchronized - 不能用
new创建新实例,也没办法通过反射绕过(JDK 12+默认禁止反射构造enum) - 如果需要带属性,直接在枚举常量后加括号传参,再写对应构造器,别用
setXXX()方法
怎么给enum加字段和方法|避免空指针和逻辑错位
很多初学者加字段后忘了初始化,或者把逻辑写在静态块里,导致顺序依赖出问题。核心原则:每个枚举常量的参数必须在声明时明确传入,字段用private final修饰,方法尽量无副作用。
例如表示HTTP状态码:
立即学习“Java免费学习笔记(深入)”;
enum HttpStatus {
OK(200, "OK"),
NOT_FOUND(404, "Not Found"),
INTERNAL_ERROR(500, "Internal Server Error");
private final int code;
private final String reason;
HttpStatus(int code, String reason) {
this.code = code;
this.reason = reason;
}
public int getCode() { return code; }
public String getReason() { return reason; }
}
- 构造器必须是
private(即使不写,默认也是),否则编译报错 - 字段必须
final,否则可能被意外修改(虽然enum实例本身不可替换,但字段可变会破坏语义) - 别在
enum里写耗时操作(如读配置、连数据库),类加载阶段卡住整个应用
switch匹配enum时漏default会怎样|编译期检查失效场景
Java 14+支持switch表达式,对enum有穷举检查——但只在你没写default分支且所有枚举常量都显式列出时才生效。一旦加了default,编译器就放弃检查,新增枚举值不会触发警告。
- 如果业务逻辑必须覆盖所有情况,删掉
default,让编译器强制你补全 - 如果确实需要兜底(比如兼容未来扩展),改用
if-else if链,并在最后抛IllegalArgumentException("unknown enum: " + e),至少运行时报错可定位 - 别用
e.name().equals("XXX")做判断——绕过类型检查,字符串拼错就静默失败
序列化enum时名字变了怎么办|JSON和RPC的兼容性坑
默认序列化用的是name(),也就是大写蛇形(如NOT_FOUND)。但API通常要小写中划线(not-found)或驼峰(notFound),这时候改name()不行——它是final的,也不能重写。
- Jackson用
@JsonValue标注一个方法(返回String),反序列化时用@JsonCreator配静态工厂方法 - Protobuf或gRPC需靠
enum值映射表,不能只靠名字;生成代码前确认.proto里的enum值和Java保持一致 - 别在
enum里存复杂对象(如List或Map)作为字段,序列化可能循环引用或膨胀体积
最麻烦的其实是跨服务版本升级:老服务发UNKNOWN,新服务没这个枚举值,直接反序列化失败。得提前约定保留UNRECOGNIZED占位符,而不是靠try-catch兜底。










