12 Enum —— 枚举类型的底层实现

Enum ------ 枚举类型的底层实现

适用版本: JDK 8 难度等级: 基础 核心概念: 枚举的本质是 final class、ordinal 与 name、EnumSet/EnumMap


一、枚举的本质

Java 中 enum 关键字定义的枚举类型,编译后实际上是一个继承自 java.lang.Enumfinal 类

java 复制代码
public enum Color { RED, GREEN, BLUE }

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

反编译验证

bash 复制代码
# 编译 Color.java
javac Color.java

# 反编译查看
javap -c -p Color.class

二、Enum 基类源码

java 复制代码
public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable {

    private final String name;       // 枚举常量的名称
    private final int ordinal;       // 枚举常量的序号(从0开始)

    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

    public final String name()  { return name; }
    public final int ordinal()  { return ordinal; }

    public String toString()    { return name; }

    public final boolean equals(Object other) {
        return this == other;  // 枚举常量是单例,== 足够
    }

    public final int hashCode() {
        return super.hashCode();  // 使用 Object 的 hashCode
    }

    // 禁止克隆------保持单例
    protected final Object clone() throws CloneNotSupportedException {
        throw new CloneNotSupportedException();
    }

    public final int compareTo(E o) {
        Enum<?> other = (Enum<?>) o;
        Enum<E> self = this;
        if (self.getClass() != other.getClass()
                && self.getDeclaringClass() != other.getDeclaringClass())
            throw new ClassCastException();
        return self.ordinal - other.ordinal;  // 按声明顺序比较
    }

    public final Class<E> getDeclaringClass() {
        Class<?> clazz = getClass();
        Class<?> zuper = clazz.getSuperclass();
        return (zuper == Enum.class) ? (Class<E>) clazz : (Class<E>) zuper;
    }

    public static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) {
        T result = enumType.enumConstantDirectory().get(name);
        if (result != null) return result;
        if (name == null) throw new NullPointerException("Name is null");
        throw new IllegalArgumentException(
                "No enum constant " + enumType.getCanonicalName() + "." + name);
    }

    // 禁止 finalize
    protected final void finalize() {}
}

三、枚举的高级特性

3.1 带属性和方法的枚举

java 复制代码
public enum HttpStatus {
    OK(200, "请求成功"),
    NOT_FOUND(404, "资源未找到"),
    INTERNAL_ERROR(500, "服务器内部错误");

    private final int code;
    private final String description;

    HttpStatus(int code, String description) {
        this.code = code;
        this.description = description;
    }

    public int getCode()          { return code; }
    public String getDescription(){ return description; }

    public static HttpStatus fromCode(int code) {
        for (HttpStatus status : values()) {
            if (status.code == code) return status;
        }
        throw new IllegalArgumentException("未知状态码: " + code);
    }
}
java 复制代码
public class EnumFieldsDemo {
    public static void main(String[] args) {
        HttpStatus status = HttpStatus.NOT_FOUND;
        System.out.println(status.getCode() + " → " + status.getDescription());

        HttpStatus resolved = HttpStatus.fromCode(200);
        System.out.println(resolved);  // OK
    }
}

3.2 枚举实现接口

java 复制代码
public enum Calculator {
    ADD {
        @Override
        public double apply(double a, double b) { return a + b; }
    },
    SUBTRACT {
        @Override
        public double apply(double a, double b) { return a - b; }
    },
    MULTIPLY {
        @Override
        public double apply(double a, double b) { return a * b; }
    },
    DIVIDE {
        @Override
        public double apply(double a, double b) {
            if (b == 0) throw new ArithmeticException("除零");
            return a / b;
        }
    };

    public abstract double apply(double a, double b);

    public static void main(String[] args) {
        System.out.println(ADD.apply(10, 5));      // 15.0
        System.out.println(MULTIPLY.apply(10, 5));  // 50.0
    }
}

四、EnumSet 与 EnumMap

两个专门为枚举设计的高效集合类:

java 复制代码
import java.util.EnumSet;
import java.util.EnumMap;

public class EnumCollectionsDemo {
    enum Day { MON, TUE, WED, THU, FRI, SAT, SUN }

    public static void main(String[] args) {
        // EnumSet ------ 内部用位向量实现,极高效
        EnumSet<Day> workdays = EnumSet.range(Day.MON, Day.FRI);
        EnumSet<Day> weekend = EnumSet.of(Day.SAT, Day.SUN);

        System.out.println("工作日: " + workdays);
        System.out.println("周末: " + weekend);

        // EnumMap ------ key 必须是枚举,内部用数组索引
        EnumMap<Day, String> schedule = new EnumMap<>(Day.class);
        schedule.put(Day.MON, "开会");
        schedule.put(Day.FRI, "汇报");

        System.out.println("周一安排: " + schedule.get(Day.MON));
    }
}

五、综合实战:策略模式的枚举实现

java 复制代码
/**
 * 使用枚举实现策略模式------免去大量 if-else
 */
public class DiscountStrategy {
    public enum DiscountType {
        NORMAL {
            @Override
            double apply(double price) { return price; }
        },
        VIP {
            @Override
            double apply(double price) { return price * 0.85; }
        },
        SVIP {
            @Override
            double apply(double price) { return price * 0.70; }
        },
        EMPLOYEE {
            @Override
            double apply(double price) { return price * 0.50; }
        };

        abstract double apply(double price);
    }

    public static double calculate(String type, double price) {
        DiscountType dt = DiscountType.valueOf(type.toUpperCase());
        return dt.apply(price);
    }

    public static void main(String[] args) {
        System.out.println("普通价格:  " + calculate("normal", 1000));
        System.out.println("VIP价格:   " + calculate("vip", 1000));
        System.out.println("SVIP价格:  " + calculate("svip", 1000));
        System.out.println("员工价格:  " + calculate("employee", 1000));
    }
}

六、面试要点

问题 关键要点
enum 的本质 继承 Enum 的 final 类,编译期生成
ordinal 的用途和风险 表示声明顺序,修改顺序会导致逻辑错误
为什么枚举能防止反射破坏单例 反射 newInstance 时会检查是否是枚举类,若是则抛异常
为什么枚举不能 clone Enum 的 clone 直接抛 CloneNotSupportedException
EnumMap/EnumSet 的高效原理 使用数组/位向量,O(1) 操作
相关推荐
工位植物人1 小时前
深入理解Java中的类、抽象类、接口与枚举类
后端
用户2181697049301 小时前
Gin (二) 参数 路由分组
后端
用户925807911481 小时前
nacos服务注册源码浅析
后端
轻刀快马1 小时前
从繁琐到极简,从幻象到本质:Spring AOP 架构演进与实战避坑指南
java·spring·架构
weixin_BYSJ19871 小时前
springboot旅游管理系统04470(附源码+开发文档+部署教程)
java·spring boot·python·算法·django·flask·旅游
8Qi81 小时前
LeetCode 209. 长度最小的子数组(Minimum Size Subarray Sum)
java·算法·leetcode·双指针·滑动窗口
方也_arkling1 小时前
【Java-Day12】接口
java·开发语言
SimonKing1 小时前
Java程序员接入AI的另一种姿势:LangChain4j
java·后端·程序员
右耳朵猫AI1 小时前
Rust技术周刊 2026年第20周
开发语言·后端·rust