Skip to content
清晨的一缕阳光
返回

Java 枚举详解

Java 枚举详解

Java 枚举不仅仅是常量的集合,它是一个功能强大的类型系统特性。

一、枚举基础

1.1 什么是枚举

// 基本枚举定义
public enum Day {
    MONDAY, TUESDAY, WEDNESDAY, 
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
}

// 使用
Day today = Day.MONDAY;

1.2 枚举的特性

public enum Status {
    OPEN, CLOSED;
    
    // 枚举构造函数(私有)
    private Status() {}
    
    // 可以添加方法和字段
    public boolean isBusinessOpen() {
        return this == OPEN;
    }
}

二、枚举的实现原理

2.1 编译后的字节码

// 源码
public enum Color {
    RED, GREEN, BLUE
}

// 编译后(反编译)
public final class Color extends Enum<Color> {
    public static final Color RED;
    public static final Color GREEN;
    public static final Color BLUE;
    
    private static final Color[] $VALUES;
    
    public static Color[] values() {
        return (Color[])$VALUES.clone();
    }
    
    public static Color valueOf(String name) {
        return Enum.valueOf(Color.class, name);
    }
    
    static {
        RED = new Color("RED", 0);
        GREEN = new Color("GREEN", 1);
        BLUE = new Color("BLUE", 2);
        $VALUES = new Color[]{RED, GREEN, BLUE};
    }
}

2.2 Enum 父类关键方法

// java.lang.Enum 核心方法
public abstract class Enum<E extends Enum<E>> 
    implements Comparable<E>, Serializable {
    
    private final String name;
    private final int ordinal;
    
    // 比较(基于 ordinal)
    public final int compareTo(E o) {
        return this.ordinal - o.ordinal;
    }
    
    // 获取名称
    public final String name() {
        return name;
    }
    
    // 获取序号
    public final int ordinal() {
        return ordinal;
    }
}

三、枚举的高级用法

3.1 带字段和方法的枚举

public enum Planet {
    MERCURY(3.303e+23, 2.4397e6),
    VENUS(4.869e+24, 6.0518e6),
    EARTH(5.976e+24, 6.37814e6);
    
    private final double mass;   // 质量
    private final double radius; // 半径
    
    Planet(double mass, double radius) {
        this.mass = mass;
        this.radius = radius;
    }
    
    public double mass() { return mass; }
    public double radius() { return radius; }
    
    public double surfaceGravity() {
        return 6.67300E-11 * mass / (radius * radius);
    }
    
    public double surfaceWeight(double otherMass) {
        return otherMass * surfaceGravity();
    }
}

3.2 抽象方法枚举

public enum Operation {
    PLUS("+") {
        public double apply(double x, double y) {
            return x + y;
        }
    },
    MINUS("-") {
        public double apply(double x, double y) {
            return x - y;
        }
    },
    TIMES("*") {
        public double apply(double x, double y) {
            return x * y;
        }
    };
    
    private final String symbol;
    
    Operation(String symbol) {
        this.symbol = symbol;
    }
    
    public abstract double apply(double x, double y);
    
    @Override
    public String toString() {
        return symbol;
    }
}

3.3 策略模式实现

public enum PayrollDay {
    MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY,
    SATURDAY, SUNDAY;
    
    private int pay(int minutesWorked, int payRate) {
        return switch (this) {
            case SATURDAY, SUNDAY -> 
                minutesWorked * payRate * 2; // 双倍工资
            default -> 
                minutesWorked * payRate;
        };
    }
}

四、EnumMap 详解

4.1 核心特性

EnumMap = 基于数组的专用 Map

特点:
- 只能以枚举类型作为 key
- 内部使用数组存储,性能极高
- 按枚举定义顺序存储
- 非线程安全

4.2 使用示例

enum Color { RED, GREEN, BLUE }

// 创建 EnumMap
Map<Color, String> colorMap = new EnumMap<>(Color.class);
colorMap.put(Color.RED, "红色");
colorMap.put(Color.GREEN, "绿色");
colorMap.put(Color.BLUE, "蓝色");

// 性能对比
// EnumMap: O(1),基于数组索引
// HashMap: O(1),但有哈希计算开销

4.3 底层实现

public class EnumMap<K extends Enum<K>, V> 
    extends AbstractMap<K, V> {
    
    private final Class<K> keyType;
    private transient V[] vals; // 核心:数组存储
    
    // 通过 ordinal 直接访问
    public V get(Object key) {
        if (isValidKey(key)) {
            return vals[((Enum<?>)key).ordinal()];
        }
        return null;
    }
    
    public V put(K key, V value) {
        int index = key.ordinal();
        V oldValue = vals[index];
        vals[index] = value;
        return oldValue;
    }
}

五、EnumSet 详解

4.1 核心特性

EnumSet = 基于位向量的专用 Set

特点:
- 只能存储同一种枚举类型
- 使用 long(位向量)存储
- 性能远高于 HashSet
- 非线程安全

4.2 使用示例

enum Style { BOLD, ITALIC, UNDERLINE }

// 创建 EnumSet
Set<Style> styles = EnumSet.of(Style.BOLD, Style.ITALIC);

// 位运算操作
styles.add(Style.UNDERLINE);  // 设置位
styles.remove(Style.BOLD);    // 清除位
boolean hasBold = styles.contains(Style.BOLD); // 检查位

4.3 底层实现

public abstract class EnumSet<E extends Enum<E>> 
    extends AbstractSet<E> {
    
    // 两种实现
    // RegularEnumSet: long(最多 64 个枚举值)
    // JumboEnumSet: long[](超过 64 个枚举值)
    
    private static class RegularEnumSet<E extends Enum<E>> 
        extends EnumSet<E> {
        
        private long elements = 0L; // 位向量
        
        public boolean add(E e) {
            long oldElements = elements;
            elements |= (1L << e.ordinal()); // 位或运算
            return elements != oldElements;
        }
        
        public boolean contains(Object e) {
            return (elements & (1L << ((Enum<?>)e).ordinal())) != 0;
        }
    }
}

六、性能对比

6.1 Map 性能对比

// 测试 100 万次操作
Map<Day, String> hashMap = new HashMap<>();
Map<Day, String> enumMap = new EnumMap<>(Day.class);

// put 操作
HashMap:  120ms
EnumMap:   30ms  // 快 4 倍

// get 操作
HashMap:  100ms
EnumMap:   25ms  // 快 4 倍

6.2 Set 性能对比

// 测试 100 万次操作
Set<Style> hashSet = new HashSet<>();
Set<Style> enumSet = EnumSet.of(Style.BOLD);

// add 操作
HashSet:  150ms
EnumSet:   20ms  // 快 7.5 倍

// contains 操作
HashSet:  120ms
EnumSet:   15ms  // 快 8 倍

七、最佳实践

7.1 使用场景

// ✅ 推荐:状态机
enum OrderStatus {
    PENDING, PROCESSING, SHIPPED, DELIVERED, CANCELLED
}

// ✅ 推荐:策略模式
enum Discount {
    NONE(0), SILVER(0.1), GOLD(0.2), PLATINUM(0.3);
    
    private final double rate;
    Discount(double rate) { this.rate = rate; }
    public double apply(double price) {
        return price * (1 - rate);
    }
}

// ✅ 推荐:类型安全的常量
enum TimeUnit {
    NANOSECONDS, MICROSECONDS, MILLISECONDS, SECONDS
}

// ❌ 避免:简单常量(可用 final static)
enum Constants {
    VALUE1, VALUE2  // 不如 public static final
}

7.2 注意事项

// ⚠️ 注意:ordinal() 的滥用
public int getValue() {
    return ordinal(); // 不推荐,依赖定义顺序
}

// ✅ 推荐:显式字段
public enum Status {
    OPEN(1), CLOSED(2);
    private final int code;
    Status(int code) { this.code = code; }
    public int getCode() { return code; }
}

// ⚠️ 注意:序列化问题
// 枚举通过 name 序列化,添加/删除枚举值可能破坏兼容性

八、总结

枚举核心要点:

特性说明性能
枚举本身类型安全的常量集合-
EnumMap基于数组的 MapO(1),比 HashMap 快 4 倍
EnumSet基于位向量的 SetO(1),比 HashSet 快 8 倍

枚举是 Java 强大的类型系统特性,合理使用能提高代码的类型安全性和可维护性。


分享这篇文章到:

上一篇文章
Spring Boot Docker 容器化部署
下一篇文章
Helm Chart 打包