枚举的定义与基本使用
支付系统里,订单状态用 0 表示待支付,用 1 表示已支付。几年后维护这段代码的同事,看到 status = 1 时,需要翻遍文档才能确认这到底是哪种状态。这种「魔数」问题,枚举能从根本上解决。
为什么使用枚举
传统常量的问题
java
// ❌ 使用常量类
public class OrderStatus {
public static final int PENDING = 0;
public static final int PAID = 1;
public static final int SHIPPED = 2;
public static final int DELIVERED = 3;
}
// 使用时容易出错
int status = 1;
if (status == OrderStatus.PAID) { } // 不安全,status 可以是任意整数枚举解决方案
java
// ✅ 使用枚举
public enum OrderStatus {
PENDING, // 待支付
PAID, // 已支付
SHIPPED, // 已发货
DELIVERED // 已送达
}
// 使用时类型安全
OrderStatus status = OrderStatus.PAID;
if (status == OrderStatus.PAID) { } // 编译器检查,类型安全基本枚举定义
语法
java
public enum 枚举名称 {
枚举值1,
枚举值2,
枚举值3
}示例
java
public enum Season {
SPRING, // 春
SUMMER, // 夏
AUTUMN, // 秋
WINTER // 冬
}
// 使用
Season season = Season.SPRING;枚举的构造方法
私有构造方法
枚举的构造方法必须是 private(默认也是 private):
java
public enum Level {
LOW(1, "低"),
MEDIUM(2, "中"),
HIGH(3, "高");
private final int code;
private final String description;
// 私有构造方法
private Level(int code, String description) {
this.code = code;
this.description = description;
}
public int getCode() {
return code;
}
public String getDescription() {
return description;
}
}使用
java
Level level = Level.HIGH;
System.out.println(level.getCode()); // 3
System.out.println(level.getDescription()); // 高枚举的方法
常用方法
java
public enum OrderStatus {
PENDING, PAID, SHIPPED, DELIVERED;
}
// 1. values() - 获取所有枚举值
OrderStatus[] statuses = OrderStatus.values();
for (OrderStatus status : statuses) {
System.out.println(status);
}
// 2. valueOf() - 根据名称获取枚举值
OrderStatus status = OrderStatus.valueOf("PAID");
// 3. ordinal() - 获取枚举值的序号(从0开始)
int ordinal = OrderStatus.PAID.ordinal(); // 1
// 4. name() - 获取枚举值名称
String name = OrderStatus.PAID.name(); // "PAID"自定义方法
java
public enum Weekday {
MONDAY("星期一"),
TUESDAY("星期二"),
WEDNESDAY("星期三"),
THURSDAY("星期四"),
FRIDAY("星期五"),
SATURDAY("星期六"),
SUNDAY("星期日");
private final String chineseName;
Weekday(String chineseName) {
this.chineseName = chineseName;
}
public String getChineseName() {
return chineseName;
}
// 判断是否工作日
public boolean isWeekday() {
return this != SATURDAY && this != SUNDAY;
}
// 判断是否周末
public boolean isWeekend() {
return this == SATURDAY || this == SUNDAY;
}
}枚举与 switch
java
public enum Operation {
ADD, SUBTRACT, MULTIPLY, DIVIDE
}
public class Calculator {
public double calculate(double a, double b, Operation op) {
switch (op) {
case ADD:
return a + b;
case SUBTRACT:
return a - b;
case MULTIPLY:
return a * b;
case DIVIDE:
if (b != 0) {
return a / b;
}
throw new ArithmeticException("除数不能为零");
default:
throw new IllegalArgumentException("未知操作");
}
}
}枚举实现接口
实现单个接口
java
public interface Describable {
String getDescription();
}
public enum Color implements Describable {
RED("红色"),
GREEN("绿色"),
BLUE("蓝色");
private final String description;
Color(String description) {
this.description = description;
}
@Override
public String getDescription() {
return description;
}
}实现多个接口
java
public interface Nameable {
String getName();
}
public interface Serializable {
byte[] serialize();
}
public enum Status implements Nameable, Serializable {
ACTIVE("活跃"),
INACTIVE("不活跃");
private final String name;
Status(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
@Override
public byte[] serialize() {
return name.getBytes();
}
}枚举在集合中的使用
EnumSet
java
import java.util.EnumSet;
// 创建 EnumSet
EnumSet<Weekday> weekdays = EnumSet.of(Weekday.MONDAY, Weekday.TUESDAY,
Weekday.WEDNESDAY, Weekday.THURSDAY, Weekday.FRIDAY);
// 添加
weekdays.add(Weekday.MONDAY);
// 移除
weekdays.remove(Weekday.FRIDAY);
// 判断
boolean isWorkday = weekdays.contains(Weekday.MONDAY);EnumMap
java
import java.util.EnumMap;
// 创建 EnumMap
EnumMap<Weekday, String> schedule = new EnumMap<>(Weekday.class);
// 添加
schedule.put(Weekday.MONDAY, "团队会议");
schedule.put(Weekday.FRIDAY, "周总结");
// 获取
String event = schedule.get(Weekday.MONDAY);
// 遍历
for (Map.Entry<Weekday, String> entry : schedule.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}枚举单例模式
最佳单例实现
java
public enum Singleton {
INSTANCE;
private String data;
public void setData(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
// 使用
Singleton instance = Singleton.INSTANCE;
instance.setData("Hello");为什么枚举单例最好
| 方式 | 线程安全 | 防反射 | 防反序列化 |
|---|---|---|---|
| 饿汉式 | ✅ | ❌ | ❌ |
| 懒汉式 | ❌ | ❌ | ❌ |
| 双重检查 | ✅ | ❌ | ❌ |
| 静态内部类 | ✅ | ❌ | ❌ |
| 枚举 | ✅ | ✅ | ✅ |
枚举与数据库
存储为数字
java
public enum OrderStatus {
PENDING, // 0
PAID, // 1
SHIPPED, // 2
DELIVERED; // 3
}
// 存储时使用 ordinal() + 1(避免从0开始)
int code = status.ordinal() + 1;
// 读取时转换
OrderStatus status = OrderStatus.values()[code - 1];存储为字符串
java
// 存储时使用 name()
String name = status.name(); // "PAID"
// 读取时转换
OrderStatus status = OrderStatus.valueOf(name);要点回顾
- 枚举用于定义固定数量的常量集合,类型安全、语义清晰
- 枚举值默认是
public static final - 枚举可以有构造方法、自定义方法和属性
- 枚举与
switch配合使用,编译器穷尽检查 - 枚举可以实现接口,每个枚举值可以有不同行为
EnumSet和EnumMap是枚举专用的集合,性能优秀- 枚举是实现单例模式的最佳方式,JVM 天然保证线程安全
