java语法之-enum
enum大观
jdk1.5版本引入了enum语法。对于我们经常使用到的enum关键字,你是不是也有很多的疑问。
带着如下的这些问题,我们一步一步揭开enum关键字的面纱。
(1) enum和Enum有什么关系?都说enum是语法糖,编译完成之后他最终长什么样子?大量使用Enum对象会有性能问题吗?
(2) 为什么推荐使用EnumMap和EnumSet?
enm真身
众所周知,enum其实是java的一个语法糖。那么她的真身到底长什么样子了?
通过反编译技术,我们一步步揭开enum神秘的面纱。
反编译enum
我们先看一个enum的定义:
/**
* @author samdyli
* @date 2019/07/11 20:32
*/
public enum Color {
RED,
GREEN,
YELLOW;
}
先用javac Color.java命令将Color类编译成字节码,然后使用javap -p -c Color.class反编译字节码。
Compiled from "Color.java"
public final class Color extends java.lang.Enum
public static final Color RED;
public static final Color GREEN;
public static final Color YELLOW;
private static final Color[] $VALUES;
public static Color[] values();
Code:
0: getstatic #1 // Field $VALUES:[LColor;
3: invokevirtual #2 // Method "[LColor;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LColor;"
9: areturn
public static Color valueOf(java.lang.String);
Code:
0: ldc #4 // class Color
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class Color
9: areturn
private Color();
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6 // Method java/lang/Enum."
6: return
static {};
Code:
0: new #4 // class Color
3: dup
4: ldc #7 // String RED
6: iconst_0
7: invokespecial #8 // Method "
10: putstatic #9 // Field RED:LColor;
13: new #4 // class Color
16: dup
17: ldc #10 // String GREEN
19: iconst_1
20: invokespecial #8 // Method "
23: putstatic #11 // Field GREEN:LColor;
26: new #4 // class Color
29: dup
30: ldc #12 // String YELLOW
32: iconst_2
33: invokespecial #8 // Method "
36: putstatic #13 // Field YELLOW:LColor;
39: iconst_3
40: anewarray #4 // class Color
43: dup
44: iconst_0
45: getstatic #9 // Field RED:LColor;
48: aastore
49: dup
50: iconst_1
51: getstatic #11 // Field GREEN:LColor;
54: aastore
55: dup
56: iconst_2
57: getstatic #13 // Field YELLOW:LColor;
60: aastore
61: putstatic #1 // Field $VALUES:[LColor;
64: return
}
从反编译代码段,我们发现enum类是一个继承Enum一个普通final类,每一个enum项被编译成一个常量(static final修饰的属性)。
Enum类
enum继承Enum类,有必要看看Enum都提供了什么功能。
简化Enum源码
public abstract class Enum
implements Comparable
private final String name; // 名称
private final int ordinal; // 序号
public final String name() { // 暴露获取名称方法
return name;
}
public final int ordinal() { // 暴露获取序号方法
return ordinal;
}
protected Enum(String name, int ordinal) { // 隐藏构造函数
this.name = name;
this.ordinal = ordinal;
}
public final boolean equals(Object other) { // equals方法
return this==other;
}
public final int compareTo(E o) { // 比较序号
Enum> other = (Enum>)o;
Enum
if (self.getClass() != other.getClass() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
}
针对Enum源码说明几点:
(1)Enum类提供了equals(==),compareTo,name和ordinal的方法。
(2) 为了阻止通过公开构造函数,序列化的方式构造Enum,Enum类覆盖readObject和readObjectNoData以及构造函数。
values
Enum源码中我们并没有发现values方法的定义和实现,其实values方法时在编译时自动增加的。从反编译源码中,反编译之后编译器新增了public static Color[] values();定义,并且在static代码块中初始化数组。
EnumMap和EnumSet
我们会遇到使用enum对象作为key的场景,这种场景推荐使用高效的EnumMap代替普通的Map。
EnumMap之所以高效是因为它使用Object数组作为存储结构。使用enum的序号(ordinal)作为数组索引进行随机读取。
摘取几段EnumMap实现核心代码,一探究竟。
public class EnumMap
implements java.io.Serializable, Cloneable {
private transient Object[] vals; // 存储
private static final Object NULL = new Object() {
public int hashCode() {
return 0;
}
public String toString() {
return "java.util.EnumMap.NULL";
}
};
public EnumMap(Class
this.keyType = keyType;
keyUniverse = getKeyUniverse(keyType);
vals = new Object[keyUniverse.length];
}
public V put(K key, V value) { // put代码
typeCheck(key);
int index = key.ordinal();
Object oldValue = vals[index];
vals[index] = maskNull(value);
if (oldValue == null)
size++;
return unmaskNull(oldValue);
}
}
enum最佳实践
《Effective Java》对于enum有一点最佳实践,分别是:
用enum代替int常量
用实例域代替序数
用EnumSet代替位域
用EnumMap代替序数索引
用接口模拟可伸缩的枚举
参考文献
通过反编译字节码来理解 Java 枚举
Effective Java-枚举和注解