枚舉實現原理
package com.own.learn.jdk.enum1;
public enum Day {
MONDAY("monday"), TUESDAY("TUESDAY"), WEDNESDAY("WEDNESDAY"),
THURSDAY("THURSDAY"), FRIDAY("FRIDAY"), SATURDAY("SATURDAY"), SUNDAY("SUNDAY");
private String name;
Day(String name) {
this.name = name;
}
}
拿出來反編譯的代碼:
javap -c com/own/learn/jdk/enum1/Day.class
public final class com.own.learn.jdk.enum1.Day extends java.lang.Enum<com.own.learn.jdk.enum1.Day> {
public static final com.own.learn.jdk.enum1.Day MONDAY;
public static final com.own.learn.jdk.enum1.Day TUESDAY;
public static final com.own.learn.jdk.enum1.Day WEDNESDAY;
public static final com.own.learn.jdk.enum1.Day THURSDAY;
public static final com.own.learn.jdk.enum1.Day FRIDAY;
public static final com.own.learn.jdk.enum1.Day SATURDAY;
public static final com.own.learn.jdk.enum1.Day SUNDAY;
public static com.own.learn.jdk.enum1.Day[] values();
Code:
0: getstatic #1 // Field $VALUES:[Lcom/own/learn/jdk/enum1/Day;
3: invokevirtual #2 // Method "[Lcom/own/learn/jdk/enum1/Day;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lcom/own/learn/jdk/enum1/Day;"
9: areturn
public static com.own.learn.jdk.enum1.Day valueOf(java.lang.String);
Code:
0: ldc #4 // class com/own/learn/jdk/enum1/Day
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class com/own/learn/jdk/enum1/Day
9: areturn
static {};
Code:
0: new #4 // class com/own/learn/jdk/enum1/Day
3: dup
4: ldc #8 // String MONDAY
6: iconst_0
7: ldc #9 // String monday
9: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
12: putstatic #11 // Field MONDAY:Lcom/own/learn/jdk/enum1/Day;
15: new #4 // class com/own/learn/jdk/enum1/Day
18: dup
19: ldc #12 // String TUESDAY
21: iconst_1
22: ldc #12 // String TUESDAY
24: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
27: putstatic #13 // Field TUESDAY:Lcom/own/learn/jdk/enum1/Day;
30: new #4 // class com/own/learn/jdk/enum1/Day
33: dup
34: ldc #14 // String WEDNESDAY
36: iconst_2
37: ldc #14 // String WEDNESDAY
39: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
42: putstatic #15 // Field WEDNESDAY:Lcom/own/learn/jdk/enum1/Day;
45: new #4 // class com/own/learn/jdk/enum1/Day
48: dup
49: ldc #16 // String THURSDAY
51: iconst_3
52: ldc #16 // String THURSDAY
54: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
57: putstatic #17 // Field THURSDAY:Lcom/own/learn/jdk/enum1/Day;
60: new #4 // class com/own/learn/jdk/enum1/Day
63: dup
64: ldc #18 // String FRIDAY
66: iconst_4
67: ldc #18 // String FRIDAY
69: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
72: putstatic #19 // Field FRIDAY:Lcom/own/learn/jdk/enum1/Day;
75: new #4 // class com/own/learn/jdk/enum1/Day
78: dup
79: ldc #20 // String SATURDAY
81: iconst_5
82: ldc #20 // String SATURDAY
84: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
87: putstatic #21 // Field SATURDAY:Lcom/own/learn/jdk/enum1/Day;
90: new #4 // class com/own/learn/jdk/enum1/Day
93: dup
94: ldc #22 // String SUNDAY
96: bipush 6
98: ldc #22 // String SUNDAY
100: invokespecial #10 // Method "<init>":(Ljava/lang/String;ILjava/lang/String;)V
103: putstatic #23 // Field SUNDAY:Lcom/own/learn/jdk/enum1/Day;
106: bipush 7
108: anewarray #4 // class com/own/learn/jdk/enum1/Day
111: dup
112: iconst_0
113: getstatic #11 // Field MONDAY:Lcom/own/learn/jdk/enum1/Day;
116: aastore
117: dup
118: iconst_1
119: getstatic #13 // Field TUESDAY:Lcom/own/learn/jdk/enum1/Day;
122: aastore
123: dup
124: iconst_2
125: getstatic #15 // Field WEDNESDAY:Lcom/own/learn/jdk/enum1/Day;
128: aastore
129: dup
130: iconst_3
131: getstatic #17 // Field THURSDAY:Lcom/own/learn/jdk/enum1/Day;
134: aastore
135: dup
136: iconst_4
137: getstatic #19 // Field FRIDAY:Lcom/own/learn/jdk/enum1/Day;
140: aastore
141: dup
142: iconst_5
143: getstatic #21 // Field SATURDAY:Lcom/own/learn/jdk/enum1/Day;
146: aastore
147: dup
148: bipush 6
150: getstatic #23 // Field SUNDAY:Lcom/own/learn/jdk/enum1/Day;
153: aastore
154: putstatic #1 // Field $VALUES:[Lcom/own/learn/jdk/enum1/Day;
157: return
}
轉換成代碼 大概是這樣的:
package com.own.learn.jdk.enum1;
public final class DayEnum extends Enum {
public static DayEnum MONDAY;
public static DayEnum TUESDAY;
public static DayEnum WEDNESDAY;
public static DayEnum THURSDAY;
public static DayEnum FRIDAY;
public static DayEnum SATURDAY;
public static DayEnum SUNDAY;
private static DayEnum $VALUES[];
static {
MONDAY = new DayEnum("MONDAY", 0);
TUESDAY = new DayEnum("TUESDAY", 1);
WEDNESDAY = new DayEnum("WEDNESDAY", 2);
THURSDAY = new DayEnum("THURSDAY", 3);
FRIDAY = new DayEnum("FRIDAY", 4);
SATURDAY = new DayEnum("SATURDAY", 5);
SUNDAY = new DayEnum("SUNDAY", 6);
$VALUES = (new DayEnum[]{
MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY
});
}
public DayEnum[] values() {
return (DayEnum[]) $VALUES.clone();
}
public DayEnum valueOf(String name) {
return (DayEnum) Enum.valueOf(com.own.learn.jdk.enum1.DayEnum.class, name);
}
protected DayEnum(String name, int ordinal) {
super(name, ordinal);
}
@Override
public String toString() {
return super.toString();
}
}
Enum類
public abstract class Enum<E extends Enum<E>>
implements Comparable<E>, Serializable {
private final String name;
public final String name() {
return name;
}
private final int ordinal;
public final int ordinal() {
return ordinal;
}
protected Enum(String name, int ordinal) {
this.name = name;
this.ordinal = ordinal;
}
public String toString() {
return name;
}
public final boolean equals(Object other) {
return this == other;
}
public final int hashCode() {
return super.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() && // optimization
self.getDeclaringClass() != other.getDeclaringClass())
throw new ClassCastException();
return self.ordinal - other.ordinal;
}
@SuppressWarnings("unchecked")
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);
}
/**
* enum classes cannot have finalize methods.
*/
protected final void finalize() {
}
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");
}
}
- ordinal: 從0到Integer.Max_value的數字,只在EnumSet和EnumMap使用,默認是0.
- equals方法:`return this==other。枚舉是常量
- compareTo:默認比較的是:ordinal
- valueOf:
enumType.enumConstantDirectory().get(name);
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;
}
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;
}
private volatile transient T[] enumConstants = null;
通過反射調用values方法得到枚舉的數組,然後轉成map
枚舉的進階用法
向enum類添加方法與自定義構造函數
public enum Day {
MONDAY("monday"), TUESDAY("TUESDAY"), WEDNESDAY("WEDNESDAY"),
THURSDAY("THURSDAY"), FRIDAY("FRIDAY"), SATURDAY("SATURDAY"), SUNDAY("SUNDAY");
private String name;
Day(String name) {
this.name = name;
}
}
enum類中定義抽象方法
package com.own.learn.jdk.enum1;
public enum DayAbstractMethod {
FOOD {
@Override
public void printName() {
System.out.println(" name is " + name());
}
};
public abstract void printName();
public static void main(String[] args) {
DayAbstractMethod.FOOD.printName();
}
}
enum類與接口
package com.own.learn.jdk.enum1;
interface Start{
void start();
}
interface End{
void end();
}
public enum EnumInterface implements Start, End{
TEST1
;
@Override
public void start() {
}
@Override
public void end() {
}
}
與單例模式
public enum EnumSingleton {
private enum MyEnumSingleton{
INSTANCE;
private Resource resource;
private MyEnumSingleton(){
resource = new Resource();
}
public Resource getResource(){
return resource;
}
}
public static Resource getResource(){
return MyEnumSingleton.INSTANCE.getResource();
}
}
多線程環境下的安全性
這個特性上面已經介紹了,因爲INSTANCE最後會被編譯器處理成static final的,並且在static模塊中進行的初始化,因此它的實例化是在class被加載階段完成,是線程安全的。這個特性也決定了枚舉單例不是lazy的,如果你的單例初始化比較費時且大多數情況下只會被引用但是不會被真正調用的話,你需要使用lazy的單例模式(上面傳統單例的方法二實現),而不要選擇枚舉單例。
不可被反射實例化
在Java中,不僅通過限制enum只能聲明private的構造方法來防止Enum被使用new進行實例化,而且還限制了使用反射的方法不能通過Constructor來newInstance一個枚舉實例。
序列化的唯一性
在序列化和反序列化的時候Enum的處理是和其他類不同的,在序列化的時候實際上只寫入了Enum的name成員,而沒有保存ordinal成員;在反序列化的時候從ObjectInputStream中讀取Enum的name成員,同時調用Enum的valueof方法傳入讀取的name得到對應的ordinal值。同時Enum是不支持自定義序列化和反序列化的,一些序列化和反序列化對應的函數例如readObject和writeObject及serialVersionUID等屬性在Enum的序列化過程中都是被忽略的。
Enum在序列化和反序列化上的特性保證了使用Enum來實現單例是經受得起序列化的攻擊的。