Skip to content

枚举的定义与基本使用

支付系统里,订单状态用 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 配合使用,编译器穷尽检查
  • 枚举可以实现接口,每个枚举值可以有不同行为
  • EnumSetEnumMap 是枚举专用的集合,性能优秀
  • 枚举是实现单例模式的最佳方式,JVM 天然保证线程安全

基于 VitePress 构建