1. Java中的枚举究竟是什么鬼?
如果从C++转java的话,会发现一个现象那就是在java中的枚举跟C++区别非常大,甚至可以说除了名字一样外,其他实现等都是非常不同的,在C++枚举更像个常数,而java的枚举更靠近正常的普通成员内部类。让我们从字节码角度看看java中的enum究竟是什么东西吧
源码:
public enum book{
BOOK1,BOOK2,BOOK3
}
字节码:
Last modified 2017-7-23; size 1026 bytes
MD5 checksum 338ca8c3927b424ae7b0acacb42fd28b
Compiled from "ChildTest.java"
public final class ChildTest$book extendsjava.lang.Enum
minor version: 0
major version: 52
flags:ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constantpool:
#1 = Class #2 // ChildTest$book
#2 = Utf8 ChildTest$book
#3 = Class #4 // java/lang/Enum
#4 = Utf8 java/lang/Enum
#5 = Utf8 BOOK1
#6 = Utf8 LChildTest$book;
#7 = Utf8 BOOK2
#8 = Utf8 BOOK3
#9 = Utf8 ENUM$VALUES
#10 = Utf8 [LChildTest$book;
#11 = Utf8
#12 = Utf8 ()V
#13 = Utf8 Code
#14 = String #5 // BOOK1
#15 = Methodref #1.#16 //ChildTest$book."
/String;I)V
#16 = NameAndType #17:#18 //"
#17 = Utf8
#18 = Utf8 (Ljava/lang/String;I)V
#19 = Fieldref #1.#20 // ChildTest$book.BOOK1:LChildTest$boo
k;
#20 = NameAndType #5:#6 // BOOK1:LChildTest$book;
#21 = String #7 // BOOK2
#22 = Fieldref #1.#23 // ChildTest$book.BOOK2:LChildTest$boo
k;
#23 = NameAndType #7:#6 // BOOK2:LChildTest$book;
#24 = String #8 // BOOK3
#25 = Fieldref #1.#26 // ChildTest$book.BOOK3:LChildTest$boo
k;
#26 = NameAndType #8:#6 // BOOK3:LChildTest$book;
#27 = Fieldref #1.#28 // ChildTest$book.ENUM$VALUES:[LChildT
est$book;
#28 = NameAndType #9:#10 // ENUM$VALUES:[LChildTest$book;
#29 = Utf8 LineNumberTable
#30 =Utf8 LocalVariableTable
#31 = Methodref #3.#16 //java/lang/Enum."
/String;I)V
#32 = Utf8 this
#33 = Utf8 values
#34 = Utf8 ()[LChildTest$book;
#35 = Methodref #36.#38 // java/lang/System.arraycopy:(Ljava/l
ang/Object;ILjava/lang/Object;II)V
#36 = Class #37 // java/lang/System
#37 = Utf8 java/lang/System
#38 = NameAndType #39:#40 // arraycopy:(Ljava/lang/Object;ILjava
/lang/Object;II)V
#39 = Utf8 arraycopy
#40 = Utf8 (Ljava/lang/Object;ILjava/lang/Object;II)V
#41 = Utf8 valueOf
#42 = Utf8 (Ljava/lang/String;)LChildTest$book;
#43 = Methodref #3.#44 // java/lang/Enum.valueOf:(Ljava/lang/
Class;Ljava/lang/String;)Ljava/lang/Enum;
#44 = NameAndType #41:#45 // valueOf:(Ljava/lang/Class;Ljava/lan
g/String;)Ljava/lang/Enum;
#45 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#46 = Utf8 SourceFile
#47 = Utf8 ChildTest.java
#48 = Utf8 Signature
#49 = Utf8 Ljava/lang/Enum
#50 = Utf8 InnerClasses
#51 = Class #52 // ChildTest
#52 = Utf8 ChildTest
#53 = Utf8 book
{
public static final ChildTest$book BOOK1;
descriptor: LChildTest$book;
flags: ACC_PUBLIC,ACC_STATIC, ACC_FINAL, ACC_ENUM
public static finalChildTest$book BOOK2;
descriptor:LChildTest$book;
flags: ACC_PUBLIC,ACC_STATIC, ACC_FINAL, ACC_ENUM
public static finalChildTest$book BOOK3;
descriptor:LChildTest$book;
flags: ACC_PUBLIC,ACC_STATIC, ACC_FINAL, ACC_ENUM
private static finalChildTest$book[] ENUM$VALUES;
descriptor:[LChildTest$book;
flags: ACC_PRIVATE,ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC
static {};
descriptor: ()V
flags:ACC_STATIC
Code:
stack=4,locals=0, args_size=0
0:new #1 // class ChildTest$book
3: dup
4:ldc #14 // String BOOK1
6: iconst_0
7:invokespecial #15 //Method "
ing;I)V
10:putstatic #19 // FieldBOOK1:LChildTest$book;
13:new #1 // class ChildTest$book
16: dup
17:ldc #21 // String BOOK2
19:iconst_1
20:invokespecial #15 //Method "
ing;I)V
23:putstatic #22 // Field BOOK2:LChildTest$book;
26:new #1 // class ChildTest$book
29: dup
30:ldc #24 // String BOOK3
32:iconst_2
33:invokespecial #15 //Method "
ing;I)V
36:putstatic #25 // FieldBOOK3:LChildTest$book;
39:iconst_3
40:anewarray #1 // class ChildTest$book
43: dup
44:iconst_0
45:getstatic #19 // FieldBOOK1:LChildTest$book;
48: aastore
49: dup
50:iconst_1
51:getstatic #22 // FieldBOOK2:LChildTest$book;
54: aastore
55: dup
56: iconst_2
57:getstatic #25 // FieldBOOK3:LChildTest$book;
60: aastore
61:putstatic #27 // FieldENUM$VALUES:[LChildTest$b
ook;
64: return
LineNumberTable:
line 20: 0
line 19: 39
LocalVariableTable:
Start Length Slot Name Signature
privateChildTest$book(java.lang.String, int);
descriptor:(Ljava/lang/String;I)V
flags:ACC_PRIVATE
Code:
stack=3,locals=3, args_size=3
0: aload_0
1: aload_1
2: iload_2
3:invokespecial #31 //Method java/lang/Enum."
(Ljava/lang/String;I)V
6: return
LineNumberTable:
line 19: 0
LocalVariableTable:
Start Length Slot Name Signature
0 7 0 this LChildTest$book;
public static ChildTest$book[] values();
descriptor: ()[LChildTest$book;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=3, args_size=0
0: getstatic #27 // FieldENUM$VALUES:[LChildTest$b
ook;
3: dup
4: astore_0
5: iconst_0
6: aload_0
7: arraylength
8: dup
9: istore_1
10: anewarray #1 // classChildTest$book
13: dup
14: astore_2
15: iconst_0
16: iload_1
17: invokestatic #35 // Methodjava/lang/System.arrayco
py:(Ljava/lang/Object;ILjava/lang/Object;II)V
20: aload_2
21: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
public static ChildTest$bookvalueOf(java.lang.String);
descriptor: (Ljava/lang/String;)LChildTest$book;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #1 // class ChildTest$book
2: aload_0
3: invokestatic #43 // Method java/lang/Enum.valueOf:(
Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class ChildTest$book
9: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
}
SourceFile:"ChildTest.java"
Signature:#49 //Ljava/lang/Enum
InnerClasses:
public static final #53= #1 of #51;//book=class ChildTest$book of class Ch
ildTest
--你会发现,编译器会替我们生成一个ChildTest$book.class文件,字节码如上文,很明显,跟普通的内部类一样。
(1)从深红色部分可以看出book这个枚举类型本质上是一个继承了一个Enum
(2)从红色部分可以看出,我们在枚举中定义的三个常量实际上就是static final ChildTest$book 类型的常量。而且编译器还帮我们添加了一个常量数组static final ChildTest$book[] ENUM$VALUES。
(3)从绿色部分可以看出,编译器在clinit方法中,调用了构造方法,分别对三个常量进行了初始化
(4)从蓝色部分可以看出,构造方法中调用了父类ENUM的构造方法。并且注意,该构造方法是私有的,也就是说其他类是无法调用的。(包括外部类)
(5)另外编译器还帮我们添加了两个public静态成员方法values()和 valueOf