枚举
约 1203 字大约 4 分钟
2026-03-27
一、枚举的概念
枚举(enum)用于定义一组固定不变的常量集合。相比使用大量 public static final 常量,枚举类型更安全、可读性更好,也更适合表达业务状态。
常见场景:
- 星期
- 季节
- 订单状态
- 用户角色
- 支付方式
二、为什么要使用枚举
如果不用枚举,很多项目会这样定义状态值:
public static final int SUCCESS = 1;
public static final int FAIL = 2;
public static final int CANCEL = 3;这种方式的问题在于:
- 类型不安全,任何
int都可以传入 - 可读性一般,难以表达业务含义
- 不方便把状态描述、编码、校验逻辑放在一起维护
枚举能够把这些信息统一封装到一个类型中。
三、枚举的基本定义
enum Season {
SPRING, SUMMER, AUTUMN, WINTER
}使用方式:
public class Demo {
public static void main(String[] args) {
Season season = Season.SUMMER;
System.out.println(season);
}
}四、枚举的本质
枚举本质上是一个特殊的类,默认继承 java.lang.Enum。因此它具有以下特点:
- 每个枚举项本质上都是该枚举类的一个对象
- 枚举构造器默认是私有的
- 枚举不能再继承其他类
- 枚举可以实现接口
例如:
Season spring = Season.SPRING;
Season summer = Season.SUMMER;这里的 SPRING、SUMMER 都是 Season 类型的对象。
五、枚举的常用 API
Season s = Season.SUMMER;
System.out.println(s.name()); // 枚举常量名
System.out.println(s.ordinal()); // 下标,从 0 开始
System.out.println(Season.valueOf("SPRING"));
for (Season item : Season.values()) {
System.out.println(item);
}说明:
name()返回定义时的名称ordinal()返回顺序编号,但不建议用于业务逻辑values()返回所有枚举项valueOf()可根据名称获取对应枚举对象
六、带属性和方法的枚举
实际开发中,枚举通常不只是一个名字,还会携带编码、描述等属性。
enum OrderStatus {
UNPAID(0, "待支付"),
PAID(1, "已支付"),
SHIPPED(2, "已发货"),
FINISHED(3, "已完成");
private final int code;
private final String desc;
OrderStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}这里有几个关键点:
- 枚举项要写在最前面
- 如果有属性和方法,枚举项列表后面要加分号
; - 枚举构造器默认私有,不能手动写成
public
七、枚举中自定义业务方法
enum OrderStatus {
UNPAID(0, "待支付"),
PAID(1, "已支付"),
SHIPPED(2, "已发货");
private final int code;
private final String desc;
OrderStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
public boolean canCancel() {
return this == UNPAID;
}
}这种写法的好处是把“状态”和“状态相关逻辑”放在一起,避免业务代码到处写 if-else。
八、枚举实现接口
枚举不能继承普通类,但可以实现接口。
interface Operation {
double apply(double x, double y);
}
enum BasicOperation implements Operation {
ADD {
@Override
public double apply(double x, double y) {
return x + y;
}
},
SUBTRACT {
@Override
public double apply(double x, double y) {
return x - y;
}
};
}这种写法在策略模式中非常常见。
九、枚举与 switch
枚举非常适合与 switch 搭配使用。
Season season = Season.SPRING;
switch (season) {
case SPRING -> System.out.println("春天");
case SUMMER -> System.out.println("夏天");
case AUTUMN -> System.out.println("秋天");
case WINTER -> System.out.println("冬天");
}相比字符串或整数状态,枚举配合 switch 更直观,也更安全。
十、枚举的典型应用场景
10.1 状态管理
例如:
- 订单状态
- 审核状态
- 任务执行状态
10.2 分类管理
例如:
- 用户类型
- 商品类型
- 通知渠道
10.3 替代魔法值
不要在代码中到处写 "SUCCESS"、1、2 这样的魔法值,而应使用统一枚举。
十一、枚举使用中的注意点
11.1 不要依赖 ordinal()
ordinal() 只是声明顺序,不适合作为数据库值或业务编码。因为一旦调整枚举顺序,旧逻辑就会出问题。
更稳妥的方式是显式定义 code 字段。
11.2 不要把枚举当成普通常量列表
如果业务中还需要描述、校验、转换逻辑,应直接写到枚举内部,提高内聚性。
11.3 数据库存储建议使用明确编码
例如在数据库中存 code 或 name,不要存 ordinal。
十二、面试常问问题
12.1 枚举可以创建对象吗
不能通过 new 创建。枚举对象在类加载时就已经固定创建好了。
12.2 枚举可以有构造器吗
可以,但构造器默认私有,不能被外部调用。
12.3 枚举可以重写方法吗
可以。既可以定义统一方法,也可以让不同枚举项分别重写方法。
十三、实践建议
- 固定集合优先使用枚举,不要滥用字符串和整数常量
- 业务枚举建议同时定义
code和desc - 与前端、数据库交互时,提前统一编码规则
- 让枚举承担部分业务判断逻辑,减少零散判断代码
