本文目录导读:

- 基础用法:定义常量
- 带字段和构造器的枚举
- 实现接口或抽象方法:状态机模式
- 策略模式(Switch 替代方案)
- 带有反向查找的枚举
- 单例模式(最安全的单例之一)
- 结合
switch的简洁用法 - 枚举实现多级的国家/地区状态(嵌套枚举)
- 推荐用法场景
Java 中的枚举(enum)是一种特殊的类,用于定义一组常量,它比传统的 public static final 常量更强大、更安全,以下是一些经典的 Java 枚举用法案例,涵盖了从基础到进阶的各种场景。
基础用法:定义常量
这是最直接的用法,代替传统的 public static final int。
// 传统方式
public static final int STATUS_PENDING = 0;
public static final int STATUS_PROCESSING = 1;
public static final int STATUS_COMPLETED = 2;
// 枚举方式
public enum OrderStatus {
PENDING, // 待处理
PROCESSING, // 处理中
COMPLETED, // 已完成
CANCELLED // 已取消
}
// 使用
OrderStatus currentStatus = OrderStatus.PENDING;
if (currentStatus == OrderStatus.COMPLETED) {
System.out.println("订单已完成");
}
优点:类型安全、可读性强、自带 name() 和 ordinal() 方法。
带字段和构造器的枚举
枚举可以像普通类一样有字段、构造器和方法,常用于将业务代码与枚举值绑定。
public enum Planet {
MERCURY(3.303e+23, 2.4397e6),
VENUS(4.869e+24, 6.0518e6),
EARTH(5.976e+24, 6.37814e6),
MARS(6.421e+23, 3.3972e6),
JUPITER(1.9e+27, 7.1492e7),
SATURN(5.688e+26, 6.0268e7),
URANUS(8.686e+25, 2.5559e7),
NEPTUNE(1.024e+26, 2.4746e7);
private final double mass; // 质量(千克)
private final double radius; // 半径(米)
Planet(double mass, double radius) {
this.mass = mass;
this.radius = radius;
}
public double getMass() { return mass; }
public double getRadius() { return radius; }
// 计算表面重力
public double surfaceGravity() {
return G * mass / (radius * radius);
}
private static final double G = 6.67300E-11;
public static void main(String[] args) {
for (Planet p : Planet.values()) {
System.out.printf("行星 %s: 质量 %.2e kg, 表面重力 %.2f m/s^2%n",
p, p.getMass(), p.surfaceGravity());
}
}
}
输出:
行星 MERCURY: 质量 3.30e+23 kg, 表面重力 3.70 m/s^2
行星 VENUS: 质量 4.87e+24 kg, 表面重力 8.87 m/s^2
...
实现接口或抽象方法:状态机模式
枚举可以定义抽象方法,每个枚举常量可以有自己的实现,非常适合实现简单的状态机。
public enum TrafficLight {
RED(30) {
@Override
public TrafficLight next() {
return GREEN;
}
},
GREEN(45) {
@Override
public TrafficLight next() {
return YELLOW;
}
},
YELLOW(5) {
@Override
public TrafficLight next() {
return RED;
}
};
private final int duration; // 持续时间(秒)
TrafficLight(int duration) {
this.duration = duration;
}
public int getDuration() { return duration; }
// 抽象方法:返回下一个状态
public abstract TrafficLight next();
}
// 使用
TrafficLight light = TrafficLight.RED;
System.out.println("当前: " + light + " (" + light.getDuration() + "秒)");
light = light.next();
System.out.println("下一个: " + light + " (" + light.getDuration() + "秒)");
输出:
当前: RED (30秒)
下一个: GREEN (45秒)
策略模式(Switch 替代方案)
用枚举消除冗长的 switch 或 if-else 业务逻辑。
public enum Operation {
PLUS {
@Override
public double apply(double x, double y) {
return x + y;
}
},
MINUS {
@Override
public double apply(double x, double y) {
return x - y;
}
},
TIMES {
@Override
public double apply(double x, double y) {
return x * y;
}
},
DIVIDE {
@Override
public double apply(double x, double y) {
return x / y;
}
};
public abstract double apply(double x, double y);
}
// 使用
Operation op = Operation.PLUS;
double result = op.apply(10, 5); // 15
优势:新增操作只需在枚举中添加一个新常量并实现 apply 方法,无需修改业务逻辑代码,符合开闭原则。
带有反向查找的枚举
根据业务字段(如代码、标签)查找枚举实例,常用于数据库持久化或对外接口。
public enum StatusCode {
SUCCESS(0, "成功"),
FAIL(1, "失败"),
TIMEOUT(2, "超时"),
UNKNOWN_ERROR(99, "未知错误");
private final int code;
private final String description;
StatusCode(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() { return code; }
public String getDescription() { return description; }
// 根据 code 反向查找枚举
public static StatusCode fromCode(int code) {
for (StatusCode s : StatusCode.values()) {
if (s.code == code) {
return s;
}
}
throw new IllegalArgumentException("未知状态码: " + code);
}
}
// 使用
StatusCode status = StatusCode.fromCode(2);
System.out.println(status.getDescription()); // 超时
单例模式(最安全的单例之一)
枚举天然支持单例,且能防止反射攻击和序列化破坏。
public enum Singleton {
INSTANCE;
private final String data = "共享数据";
public String getData() {
return data;
}
public void doSomething() {
System.out.println("执行单例方法");
}
}
// 使用
Singleton singleton = Singleton.INSTANCE;
singleton.doSomething();
为什么安全:JVM 保证枚举实例唯一创建,readResolve 机制自动处理序列化,反射 newInstance 会直接抛出 IllegalArgumentException。
结合 switch 的简洁用法
虽然枚举可以自带方法,但在很多简单脚本式代码中搭配 switch 仍然很方便。
public enum Day {
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
}
// 使用
Day day = Day.SATURDAY;
switch (day) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
case THURSDAY:
case FRIDAY:
System.out.println("工作日");
break;
case SATURDAY:
case SUNDAY:
System.out.println("周末");
break;
}
注意:switch 使用枚举时,case 中不需要写枚举类型名(如 Day.MONDAY → MONDAY),因为编译器已经知道上下文。
枚举实现多级的国家/地区状态(嵌套枚举)
枚举可以包含枚举,实现更复杂的常量分类。
public enum Country {
CHINA("中国") {
@Override
public Province[] getProvinces() {
return Province.values(); // 只包含中国省份
}
},
USA("美国") {
@Override
public Province[] getProvinces() {
return new Province[0]; // 美国没有省份(实际有州,这里简化)
}
};
private final String chineseName;
Country(String chineseName) {
this.chineseName = chineseName;
}
public String getChineseName() { return chineseName; }
// 抽象方法:每个国家返回自己的省级枚举
public abstract Province[] getProvinces();
// 省份枚举(嵌套在 Country 中,也可以独立)
public enum Province {
BEIJING("北京"),
SHANGHAI("上海"),
GUANGDONG("广东");
// ...
Province(String name) { this.name = name; }
private final String name;
public String getName() { return name; }
}
}
// 使用
Country country = Country.CHINA;
for (Country.Province p : country.getProvinces()) {
System.out.println(country.getChineseName() + " -> " + p.getName());
}
推荐用法场景
| 场景 | 推荐枚举用法 | 案例参考 |
|---|---|---|
| 一组简单常量 | 基础枚举 | 案例 1 |
| 常量携带额外数据 | 带字段构造器 | 案例 2 |
| 状态流转 / 策略 | 抽象方法 | 案例 3, 4 |
| 数据库持久化 / 接口 | 反向查找(fromCode) |
案例 5 |
| 绝对安全的单例 | 单例枚举 | 案例 6 |
| 简单分支判断 | switch + 枚举 |
案例 7 |
| 复杂分类的常量 | 嵌套枚举 | 案例 8 |
枚举是 Java 中非常重要的语言特性,合理使用可以写出更清晰、安全且易于维护的代码。