Enum
枚举用过吧? 没用过? 那没事了
test实例
public class TestEnum {
public static void main(String[] args) {
MyEnum.A.print();
}
}
enum MyEnum {
A, B, C, D;
public void print() {
System.out.println("I am " + this);
}
}
javac
在意料之中
javap
没有合适的反编译工具,只能看字节码
//父类
final class MyEnum extends java.lang.Enum<MyEnum> {
public static final MyEnum A;
public static final MyEnum B;
public static final MyEnum C;
public static final MyEnum D;
private static final MyEnum[] $VALUES;
public static MyEnum[] values();
Code:
0: getstatic #1 // Field $VALUES:[LMyEnum;
3: invokevirtual #2 // Method "[LMyEnum;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[LMyEnum;"
9: areturn
#return (MyEnum[])$VALUES.clone();
public static MyEnum valueOf(java.lang.String);
Code:
0: ldc #4 // class MyEnum
2: aload_0
3: invokestatic #5 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #4 // class MyEnum
9: areturn
#return (MyEnum)Enum.valueOf(str);
private MyEnum();
Code:
0: aload_0
1: aload_1
2: iload_2
3: invokespecial #6 // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
6: return
super(name,ordinal);
public void print();
Code:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokedynamic #8, 0 // InvokeDynamic #0:makeConcatWithConstants:(LMyEnum;)Ljava/lang/String;
9: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
static {};
Code:
# A = new MyEnum("A",0);
0: new #4 // class MyEnum
3: dup
4: ldc #10 // String A
6: iconst_0
7: invokespecial #11 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #12 // Field A:LMyEnum;
# B = new MyEnum("B",1);
13: new #4 // class MyEnum
16: dup
17: ldc #13 // String B
19: iconst_1
20: invokespecial #11 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #14 // Field B:LMyEnum;
# C = new MyEnum("C",2);
26: new #4 // class MyEnum
29: dup
30: ldc #15 // String C
32: iconst_2
33: invokespecial #11 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #16 // Field C:LMyEnum;
# D = new MyEnum("D",3);
39: new #4 // class MyEnum
42: dup
43: ldc #17 // String D
45: iconst_3
46: invokespecial #11 // Method "<init>":(Ljava/lang/String;I)V
49: putstatic #18 // Field D:LMyEnum;
# $VLUES = new MyEnum[4];
52: iconst_4
53: anewarray #4 // class MyEnum
56: dup
# $VLUES[0] = A;
57: iconst_0
58: getstatic #12 // Field A:LMyEnum;
61: aastore
# $VLUES[1] = B;
62: dup
63: iconst_1
64: getstatic #14 // Field B:LMyEnum;
67: aastore
# $VLUES[2] = C;
68: dup
69: iconst_2
70: getstatic #16 // Field C:LMyEnum;
73: aastore
# $VLUES[3] = D;
74: dup
75: iconst_3
76: getstatic #18 // Field D:LMyEnum;
79: aastore
80: putstatic #1 // Field $VALUES:[LMyEnum;
83: return
}
等价代码
final class MyEnum extends java.lang.Enum<MyEnum> {
public static final MyEnum A;
public static final MyEnum B;
public static final MyEnum C;
public static final MyEnum D;
private static final MyEnum[] $VALUES;
static {
A = new MyEnum("A", 0);
B = new MyEnum("B", 1);
C = new MyEnum("C", 2);
D = new MyEnum("D", 3);
$VALUES = new MyEnum[4];
$VALUES[0] = A;
$VALUES[1] = B;
$VALUES[2] = C;
$VALUES[3] = D;
}
public static MyEnum[] values() {
return (MyEnum[]) $VALUES.clone();
}
private MyEnum(String name, int ordinal) {
super(name, ordinal);
}
public void print() {
System.out.println("I am " + this);
}
}
解密枚举单例
我们解密一下java enum是如何防止反射和反序列化的
- 反射new实例
- 反序列化
由于values方法调用的native的clone方法,所以枚举反序列是由jvm确保的.
因此不难得出下方代码的输出是true
public class TestEnum {
public static void main(String[] args) throws Exception {
MyEnum before = MyEnum.A;
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("a.obj"));
out.writeObject(MyEnum.A);
ObjectInputStream in = new ObjectInputStream(new FileInputStream("a.obj"));
MyEnum a = (MyEnum) in.readObject();
System.out.println(before == a);
}
}
enum MyEnum {
A, B, C, D;
public void print() {
System.out.println("I am " + this);
}
}
所以effective java一书中有提到使用枚举实现单例是最优,因为确实能实现全局唯一,无论是反射还是序列化都不能破坏其单例的特性.