java枚举是编译器生成的final类,不可new实例化,所有常量在类加载时初始化;构造器默认private;values()每次返回新数组,valueof()仅匹配name()且区分大小写;switch中使用枚举更安全,编译期检查完整性;json序列化默认用name()而非tostring()。

Java枚举不是普通类,不能用 new 实例化
Java 枚举本质是编译器生成的 final 类,所有枚举常量在类加载时就完成初始化。试图写 new Color() 或 Color c = new Color("RED") 会直接报编译错误:Cannot instantiate the type Color。
正确做法是直接使用已声明的枚举常量:
enum Color { RED, GREEN, BLUE }
Color c = Color.RED;
- 枚举构造器必须是
private(省略也默认私有) - 如果定义了带参构造器,每个常量必须显式调用,比如
RED("red") - 枚举实例在 JVM 中全局唯一,天然单例,适合状态标识、协议码等场景
枚举方法里调用 values() 和 valueOf() 的坑
values() 返回数组副本,每次调用都新建数组;valueOf(String) 区分大小写且只匹配常量名,不支持自定义字段值查找。
常见错误:用 Color.valueOf("red") 报 IllegalArgumentException,因为实际常量名是 RED,不是小写字符串。
立即学习“Java免费学习笔记(深入)”;
- 需要按业务字段查枚举?得自己写静态查找方法,比如
findByCode(int code) - 频繁调用
values()?缓存结果,或改用EnumSet.allOf(Color.class)(返回不可变集合,更轻量) -
valueOf()只能用于反序列化或调试,生产逻辑中避免依赖它做用户输入解析
在 switch 中用枚举比用 int 常量更安全
Java 7+ 支持枚举直接进 switch,编译器会在编译期检查是否遗漏 case,还能防止传入非法整数值。
对比:switch(status) { case 1: ... case 2: ... } 完全无法阻止传入 status = 999;而 switch(color) { case RED: ... case GREEN: ... } 编译器知道只有三个可能值。
- 如果未来新增枚举常量,没补全 switch 分支,IDE 会警告(配合
-Xlint:switch) - 不要在 switch 里写
default:来兜底——这反而掩盖了漏处理新枚举值的问题 - 枚举 switch 编译后仍是跳转表(tableswitch),性能不输 int
序列化枚举字段时,JSON 库默认输出的是 name() 而不是 toString()
像 Jackson、Gson 默认把枚举序列化成 "RED",不是 "Red Color",哪怕你重写了 toString()。这是设计使然:枚举的 name() 是稳定标识,toString() 是可变描述。
想输出自定义字符串?别靠重写 toString(),那对 JSON 序列化无效。
- Jackson:加
@JsonValue注解到 getter 方法上 - Gson:注册
TypeAdapter,或用@SerializedName标注每个常量 - 如果只是想让日志/调试更友好,
toString()仍可照常使用,但别指望它影响序列化行为
枚举的序列化语义和运行时语义是两套逻辑,混淆这两者是线上出问题最隐蔽的来源之一。










