枚举是我们在编码过程中常用的,比如电影有多种类型:喜剧片、动作片、恐怖片、爱情片、纪录片等等。在这种场景当中,我们可以编写一个枚举类(如MovieType)来进行表示。
本文借助MovieType这个枚举类,通过查看编译后的内容,来简单探究一下JDK枚举类的基本原理。主要包含如下几个部分:
一、枚举编译之后是什么样子的?(无abstract方法)
编写一个名字为MovieType的枚举类,如:
package com.wangmengjun.tutorial.enums;
public enum MovieType {
ACTION(1,"动作片"),COMODY(2,"喜剧片");
private Integer code;
private String type;
private MovieType(Integer code, String type) {
this.code = code;
this.type = type;
}
/**
* @return the code
*/
public Integer getCode() {
return code;
}
/**
* @return the type
*/
public String getType() {
return type;
}
}
使用javap -c -v MovieType.class,查看编译后的内容:
Last modified 2019年8月26日; size 1575 bytes
MD5 checksum 0f92ec5e47d72e1da315271952535909
Compiled from "MovieType.java"
public final class com.wangmengjun.tutorial.enums.MovieType extends java.lang.Enum<com.wangmengjun.tutorial.enums.MovieType>
minor version: 0
major version: 49
flags: (0x4031) ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
this_class: #1 // com/wangmengjun/tutorial/enums/MovieType
super_class: #3 // java/lang/Enum
interfaces: 0, fields: 5, methods: 6, attributes: 2
Constant pool:
#1 = Class #2 // com/wangmengjun/tutorial/enums/MovieType
#2 = Utf8 com/wangmengjun/tutorial/enums/MovieType
#3 = Class #4 // java/lang/Enum
#4 = Utf8 java/lang/Enum
#5 = Utf8 ACTION
#6 = Utf8 Lcom/wangmengjun/tutorial/enums/MovieType;
#7 = Utf8 COMODY
#8 = Utf8 code
#9 = Utf8 Ljava/lang/Integer;
#10 = Utf8 type
#11 = Utf8 Ljava/lang/String;
#12 = Utf8 ENUM$VALUES
#13 = Utf8 [Lcom/wangmengjun/tutorial/enums/MovieType;
#14 = Utf8 <clinit>
#15 = Utf8 ()V
#16 = Utf8 Code
#17 = String #5 // ACTION
#18 = Methodref #19.#21 // java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
#19 = Class #20 // java/lang/Integer
#20 = Utf8 java/lang/Integer
#21 = NameAndType #22:#23 // valueOf:(I)Ljava/lang/Integer;
#22 = Utf8 valueOf
#23 = Utf8 (I)Ljava/lang/Integer;
#24 = String #25 // 动作片
#25 = Utf8 动作片
#26 = Methodref #1.#27 // com/wangmengjun/tutorial/enums/MovieType."<init>":(Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
#27 = NameAndType #28:#29 // "<init>":(Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
#28 = Utf8 <init>
#29 = Utf8 (Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
#30 = Fieldref #1.#31 // com/wangmengjun/tutorial/enums/MovieType.ACTION:Lcom/wangmengjun/tutorial/enums/MovieType;
#31 = NameAndType #5:#6 // ACTION:Lcom/wangmengjun/tutorial/enums/MovieType;
#32 = String #7 // COMODY
#33 = String #34 // 喜剧片
#34 = Utf8 喜剧片
#35 = Fieldref #1.#36 // com/wangmengjun/tutorial/enums/MovieType.COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
#36 = NameAndType #7:#6 // COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
#37 = Fieldref #1.#38 // com/wangmengjun/tutorial/enums/MovieType.ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
#38 = NameAndType #12:#13 // ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
#39 = Utf8 LineNumberTable
#40 = Utf8 LocalVariableTable
#41 = Methodref #3.#42 // java/lang/Enum."<init>":(Ljava/lang/String;I)V
#42 = NameAndType #28:#43 // "<init>":(Ljava/lang/String;I)V
#43 = Utf8 (Ljava/lang/String;I)V
#44 = Fieldref #1.#45 // com/wangmengjun/tutorial/enums/MovieType.code:Ljava/lang/Integer;
#45 = NameAndType #8:#9 // code:Ljava/lang/Integer;
#46 = Fieldref #1.#47 // com/wangmengjun/tutorial/enums/MovieType.type:Ljava/lang/String;
#47 = NameAndType #10:#11 // type:Ljava/lang/String;
#48 = Utf8 this
#49 = Utf8 getCode
#50 = Utf8 ()Ljava/lang/Integer;
#51 = Utf8 getType
#52 = Utf8 ()Ljava/lang/String;
#53 = Utf8 values
#54 = Utf8 ()[Lcom/wangmengjun/tutorial/enums/MovieType;
#55 = Methodref #56.#58 // java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
#56 = Class #57 // java/lang/System
#57 = Utf8 java/lang/System
#58 = NameAndType #59:#60 // arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
#59 = Utf8 arraycopy
#60 = Utf8 (Ljava/lang/Object;ILjava/lang/Object;II)V
#61 = Utf8 (Ljava/lang/String;)Lcom/wangmengjun/tutorial/enums/MovieType;
#62 = Methodref #3.#63 // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#63 = NameAndType #22:#64 // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#64 = Utf8 (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
#65 = Utf8 SourceFile
#66 = Utf8 MovieType.java
#67 = Utf8 Signature
#68 = Utf8 Ljava/lang/Enum<Lcom/wangmengjun/tutorial/enums/MovieType;>;
{
public static final com.wangmengjun.tutorial.enums.MovieType ACTION;
descriptor: Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final com.wangmengjun.tutorial.enums.MovieType COMODY;
descriptor: Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=6, locals=0, args_size=0
0: new #1 // class com/wangmengjun/tutorial/enums/MovieType
3: dup
4: ldc #17 // String ACTION
6: iconst_0
7: iconst_1
8: invokestatic #18 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11: ldc #24 // String 动作片
13: invokespecial #26 // Method "<init>":(Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
16: putstatic #30 // Field ACTION:Lcom/wangmengjun/tutorial/enums/MovieType;
19: new #1 // class com/wangmengjun/tutorial/enums/MovieType
22: dup
23: ldc #32 // String COMODY
25: iconst_1
26: iconst_2
27: invokestatic #18 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
30: ldc #33 // String 喜剧片
32: invokespecial #26 // Method "<init>":(Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
35: putstatic #35 // Field COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
38: iconst_2
39: anewarray #1 // class com/wangmengjun/tutorial/enums/MovieType
42: dup
43: iconst_0
44: getstatic #30 // Field ACTION:Lcom/wangmengjun/tutorial/enums/MovieType;
47: aastore
48: dup
49: iconst_1
50: getstatic #35 // Field COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
53: aastore
54: putstatic #37 // Field ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
57: return
LineNumberTable:
line 5: 0
line 3: 38
LocalVariableTable:
Start Length Slot Name Signature
public java.lang.Integer getCode();
descriptor: ()Ljava/lang/Integer;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #44 // Field code:Ljava/lang/Integer;
4: areturn
LineNumberTable:
line 20: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/wangmengjun/tutorial/enums/MovieType;
public java.lang.String getType();
descriptor: ()Ljava/lang/String;
flags: (0x0001) ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #46 // Field type:Ljava/lang/String;
4: areturn
LineNumberTable:
line 27: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcom/wangmengjun/tutorial/enums/MovieType;
public static com.wangmengjun.tutorial.enums.MovieType[] values();
descriptor: ()[Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=3, args_size=0
0: getstatic #37 // Field ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
3: dup
4: astore_0
5: iconst_0
6: aload_0
7: arraylength
8: dup
9: istore_1
10: anewarray #1 // class com/wangmengjun/tutorial/enums/MovieType
13: dup
14: astore_2
15: iconst_0
16: iload_1
17: invokestatic #55 // Method java/lang/System.arraycopy:(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 com.wangmengjun.tutorial.enums.MovieType valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #1 // class com/wangmengjun/tutorial/enums/MovieType
2: aload_0
3: invokestatic #62 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class com/wangmengjun/tutorial/enums/MovieType
9: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
}
SourceFile: "MovieType.java"
Signature: #68 // Ljava/lang/Enum<Lcom/wangmengjun/tutorial/enums/MovieType;>;
从上述字节码信息可以看出如下几点内容:
public final class com.wangmengjun.tutorial.enums.MovieType
extends java.lang.Enum<com.wangmengjun.tutorial.enums.MovieType>
public static final com.wangmengjun.tutorial.enums.MovieType ACTION;
descriptor: Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
public static final com.wangmengjun.tutorial.enums.MovieType COMODY;
descriptor: Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x4019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM
#35 = Fieldref #1.#36 // com/wangmengjun/tutorial/enums/MovieType.COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
#36 = NameAndType #7:#6 // COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
#37 = Fieldref #1.#38 // com/wangmengjun/tutorial/enums/MovieType.ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
#38 = NameAndType #12:#13 // ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
#39 = Utf8 LineNumberTable
等价于
public static final MovieType ACTION;
public static final MovieType COMODY;
private static final MovieType ENUM$VALUES[];
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=6, locals=0, args_size=0
0: new #1 // class com/wangmengjun/tutorial/enums/MovieType
3: dup
4: ldc #17 // String ACTION
6: iconst_0
7: iconst_1
8: invokestatic #18 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
11: ldc #24 // String 动作片
13: invokespecial #26 // Method "<init>":(Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
16: putstatic #30 // Field ACTION:Lcom/wangmengjun/tutorial/enums/MovieType;
19: new #1 // class com/wangmengjun/tutorial/enums/MovieType
22: dup
23: ldc #32 // String COMODY
25: iconst_1
26: iconst_2
27: invokestatic #18 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
30: ldc #33 // String 喜剧片
32: invokespecial #26 // Method "<init>":(Ljava/lang/String;ILjava/lang/Integer;Ljava/lang/String;)V
35: putstatic #35 // Field COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
38: iconst_2
39: anewarray #1 // class com/wangmengjun/tutorial/enums/MovieType
42: dup
43: iconst_0
44: getstatic #30 // Field ACTION:Lcom/wangmengjun/tutorial/enums/MovieType;
47: aastore
48: dup
49: iconst_1
50: getstatic #35 // Field COMODY:Lcom/wangmengjun/tutorial/enums/MovieType;
53: aastore
54: putstatic #37 // Field ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
57: return
LineNumberTable:
line 5: 0
line 3: 38
LocalVariableTable:
Start Length Slot Name Signature
等价于:
static
{
ACTION = new MovieType("ACTION", 0, Integer.valueOf(1), "动作片");
COMODY = new MovieType("COMODY", 1, Integer.valueOf(2), "喜剧片");
ENUM$VALUES = (new MovieType[] {
ACTION, COMODY
});
}
public static com.wangmengjun.tutorial.enums.MovieType[] values();
descriptor: ()[Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=5, locals=3, args_size=0
0: getstatic #37 // Field ENUM$VALUES:[Lcom/wangmengjun/tutorial/enums/MovieType;
3: dup
4: astore_0
5: iconst_0
6: aload_0
7: arraylength
8: dup
9: istore_1
10: anewarray #1 // class com/wangmengjun/tutorial/enums/MovieType
13: dup
14: astore_2
15: iconst_0
16: iload_1
17: invokestatic #55 // Method java/lang/System.arraycopy:(Ljava/lang/Object;ILjava/lang/Object;II)V
20: aload_2
21: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
通过System.arraycopy方法来实现。
public static com.wangmengjun.tutorial.enums.MovieType valueOf(java.lang.String);
descriptor: (Ljava/lang/String;)Lcom/wangmengjun/tutorial/enums/MovieType;
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=1
0: ldc #1 // class com/wangmengjun/tutorial/enums/MovieType
2: aload_0
3: invokestatic #62 // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
6: checkcast #1 // class com/wangmengjun/tutorial/enums/MovieType
9: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
等价于:
public static MovieType valueOf(String s)
{
return (MovieType)Enum.valueOf(com/wangmengjun/tutorial/enums/MovieType, s);
}
其是通过调用Enum的valueOf方法实现的。可以简单测试一下:
public class EnumMain {
public static void main(String[] args) {
MovieType comodyType = MovieType.valueOf(MovieType.COMODY.name());
System.out.println(comodyType.getCode() +":" + comodyType.getType());
}
}
debug一下,可以看到调用到Enum.valueof方法:
直观点,可以通过反编译工具,查看class文件如下:
public final class com.wangmengjun.tutorial.enums.MovieType
extends java.lang.Enum<com.wangmengjun.tutorial.enums.MovieType>
{
public static final MovieType ACTION;
public static final MovieType COMODY;
private Integer code;
private String type;
private static final MovieType ENUM$VALUES[];
private MovieType(String s, int i, Integer code, String type)
{
super(s, i);
this.code = code;
this.type = type;
}
public Integer getCode()
{
return code;
}
public String getType()
{
return type;
}
public static MovieType[] values()
{
MovieType amovietype[];
int i;
MovieType amovietype1[];
System.arraycopy(amovietype = ENUM$VALUES, 0, amovietype1 = new MovieType[i = amovietype.length], 0, i);
return amovietype1;
}
public static MovieType valueOf(String s)
{
return (MovieType)Enum.valueOf(com/wangmengjun/tutorial/enums/MovieType, s);
}
static
{
ACTION = new MovieType("ACTION", 0, Integer.valueOf(1), "动作片");
COMODY = new MovieType("COMODY", 1, Integer.valueOf(2), "喜剧片");
ENUM$VALUES = (new MovieType[] {
ACTION, COMODY
});
}
}
通过上述的描述,相信大家对无abstract的枚举,编译后是什么样子的?已经有了一个了解。
我们知道枚举类是可以包含抽象方法的,我们看看有什么不一样的底层实现吧。
二、枚举编译之后是什么样子的?(有abstract方法)
修改一下电影类型MovieType,增加一个descSomething的抽象方法,如下代码所示:
package com.wangmengjun.tutorial.enums;
public enum MovieType {
ACTION(1,"动作片"){
@Override
String descSomething() {
return "action description";
}
},COMODY(2,"喜剧片") {
@Override
String descSomething() {
return "comody description";
}
};
private Integer code;
private String type;
private MovieType(Integer code, String type) {
this.code = code;
this.type = type;
}
/**
* @return the code
*/
public Integer getCode() {
return code;
}
/**
* @return the type
*/
public String getType() {
return type;
}
abstract String descSomething();
}
查看class目录,我们可以发现多了2个内部类,MovieType$1.class和MovieType$2.class,分别代码枚举类定义ACTION和COMODY。
因为和第一部分的步骤类似,有兴趣的读者可以使用javap来查看字节码内容。本文直接通过反编译工具查看上述3个class的内容。
// Decompiled by Jad v1.5.8e2. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://kpdus.tripod.com/jad.html
// Decompiler options: packimports(3) fieldsfirst ansi space
// Source File Name: MovieType.java
package com.wangmengjun.tutorial.enums;
public abstract class MovieType extends Enum
{
public static final MovieType ACTION;
public static final MovieType COMODY;
private Integer code;
private String type;
private static final MovieType ENUM$VALUES[];
private MovieType(String s, int i, Integer code, String type)
{
super(s, i);
this.code = code;
this.type = type;
}
public Integer getCode()
{
return code;
}
public String getType()
{
return type;
}
abstract String descSomething();
public static MovieType[] values()
{
MovieType amovietype[];
int i;
MovieType amovietype1[];
System.arraycopy(amovietype = ENUM$VALUES, 0, amovietype1 = new MovieType[i = amovietype.length], 0, i);
return amovietype1;
}
public static MovieType valueOf(String s)
{
return (MovieType)Enum.valueOf(com/wangmengjun/tutorial/enums/MovieType, s);
}
MovieType(String s, int i, Integer integer, String s1, MovieType movietype)
{
this(s, i, integer, s1);
}
static
{
ACTION = new MovieType("ACTION", 0, Integer.valueOf(1), "动作片") {
String descSomething()
{
return "action description";
}
};
COMODY = new MovieType("COMODY", 1, Integer.valueOf(2), "喜剧片") {
String descSomething()
{
return "comody description";
}
};
ENUM$VALUES = (new MovieType[] {
ACTION, COMODY
});
}
}
package com.wangmengjun.tutorial.enums;
// Referenced classes of package com.wangmengjun.tutorial.enums:
// MovieType
class MovieType$1 extends MovieType
{
String descSomething()
{
return "action description";
}
MovieType$1(String s, int i, Integer $anonymous0, String $anonymous1)
{
super(s, i, $anonymous0, $anonymous1, null);
}
}
package com.wangmengjun.tutorial.enums;
// Referenced classes of package com.wangmengjun.tutorial.enums:
// MovieType
class MovieType$2 extends MovieType
{
String descSomething()
{
return "comody description";
}
MovieType$2(String s, int i, Integer $anonymous0, String $anonymous1)
{
super(s, i, $anonymous0, $anonymous1, null);
}
}
从上述反编译后的代码可以看出,增加了abstract方法之后,与无abstract的枚举,发生了如下几个变化:
三、小结
通过上述两个部分的介绍,我们可以看出枚举本质上是通过普通的类来实现的,只是编译器为我们进行了处理。主要有如下几个特点: