Enum深入解析

本文深入解析Java中的枚举(enum)关键字,阐述其与`java.lang.Enum`的关系,以及编译器如何处理。从类签名、枚举值与数组、静态代码块、values和valueOf方法,到内部实现机制和最佳实践。
摘要由CSDN通过智能技术生成

Enum深入解析

enum 关键字和 java.lang.Enum 的关系

enum 是编译器的语法糖,这一点可以用 javap 反编译验证

我们使用 enum 关键字定义了一个枚举,经过 javac 编译,编译器会对其进行加工,具体如下:

  1. 类的签名:final class Demo extends java.lang.Enum<Demo>

  2. 枚举值

    public static final Demo A; // 枚举实例

  3. 枚举数组

    private static final Demo[] $VALUES; // 通过反汇编可以知道,在静态代码块中初始化枚举值的同时,并放入枚举数组中

  4. 静态代码块

    static {} // 反汇编得知:1. new 对象 2. 初始化枚举值 3. 初始化枚举数组

  5. values 方法

    public static Demo[] values(); // 反汇编得知:1. 对枚举数组调用虚方法 clone() 2. 类型转换 3. 返回

  6. valueOf(String name) 方法

    public static Demo valueOf(java.lang.String); // 里面调用父类的 valueOf(Class enumType, String name)实现

  7. 如果没有私有构造器,则添加私有构造器

    private Demo (java.lang.String, int); // 调用父类唯一构造器实现

  8. 如果有私有构造器,则添加新的私有构造器
    如果原来构造器为 private Demo(java.lang.String), 则新构造器为 private Demo(java.lang.String, int, java.lang.String)

Enum解析

类签名

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable

实现了 Comparable 接口,说明相同类型的枚举对象可以比较(实际是比较 ordinal 大小,也就是定义的顺序)

实现了Serializable,说明可以被序列化

属性

/**
 * 枚举常量的字符串名称
 */
private final String name;

/**
 * 枚举常量在定义时的顺序,用于基于枚举的复杂数据结构,如 java.util.EnumSet、java.util.EnumMap
 */
private final int ordinal;

两个属性有对应的访问方法 name() 和 ordinal(),一般建议用 toString() 方法来访问 name 属性,因为 toString 方法的默认实现是返回 name 值,而且可以被覆盖

方法

public final boolean equals(Object other) {
    return this==other;		// 默认情况下调用 equal 和 == 是相同的
}

protected final Object clone() throws CloneNotSupportedException {
	throw new CloneNotSupportedException();		// 对象不可以克隆,保证全局唯一(单例状态)
}

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);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    throw new InvalidObjectException("can't deserialize enum");		// 默认不支持序列化
}

private void readObjectNoData() throws ObjectStreamException {
    throw new InvalidObjectException("can't deserialize enum");		// 默认不支持序列化
}

valueOf 方法调用了 Class 的 enumConstantDirectory() 方法获取对象,然后进行校验,返回

enumConstantDirectory() 的源码:

private volatile transient Map<String, T> enumConstantDirectory = null;

Map<String, T> enumConstantDirectory() {
    if (enumConstantDirectory == null) {
        T[] universe = getEnumConstantsShared();
        if (universe == null)
            throw new IllegalArgumentException(
                getName() + " is not an enum type");
        Map<String, T> m = new HashMap<>(2 * universe.length);
        for (T constant : universe)
            m.put(((Enum<?>)constant).name(), constant);
        enumConstantDirectory = m;
    }
    return enumConstantDirectory;
}

private volatile transient T[] enumConstants = null;

T[] getEnumConstantsShared() {
    if (enumConstants == null) {
        if (!isEnum()) return null;
        try {
            final Method values = getMethod("values");
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedAction<Void>() {
                    public Void run() {
                        values.setAccessible(true);
                        return null;
                    }
                });
            @SuppressWarnings("unchecked")
            T[] temporaryConstants = (T[])values.invoke(null);
            enumConstants = temporaryConstants;
        }
        // These can happen when users concoct enum-like classes
        // that don't comply with the enum spec.
        catch (InvocationTargetException | NoSuchMethodException |
               IllegalAccessException ex) { return null; }
    }
    return enumConstants;
}

enumConstantDirectory() 方法通过懒加载的方式获取类下的枚举数组,再根据(枚举名称、枚举实例)放入Map;

在获取类下的枚举数组时,通过懒加载方式,反射 Enum 中的 values() 方法,获取

拓展

通过源码得知 enum 的 valueOf 方法是调用 Enum 的 valueOf 方法实现,Enum 的 valueOf 方法实现中反射调用 enum 的 values 方法;enum 中的 values 方法是 javac 帮助生成的;那么我们自己编写一个类继承 Enum,而其中不写 values 方法,在调用 valueOf 方法时会怎样?理论上来说会抛出NoSuchMethodException,但是实际上,当你这么做时,你会发现 IDE 给你报了个错:Classes cannot directly extend ‘java.lang.Enum’

总结

  1. 枚举是单例模式的最佳实践,在考虑单例模式设计的时候,请优先考虑枚举
  2. 在 values 方法中调用了 clone 方法,保证枚举数组不会被篡改、同时也增加了内存的开销
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值