Java:从字节码看Enum类型
Posted bdmh
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java:从字节码看Enum类型相关的知识,希望对你有一定的参考价值。
public abstract class Enum<E extends java.lang.Enum<E>>
implements Comparable<E>, Serializable
private final String name;
private final int ordinal;
Enum类型包含两个属性。
name:定义的每个属性的字面文字。
ordinal:每个属性的值,默认从0开始。
枚举类型初始如下:
protected Enum(String name, int ordinal)
this.name = name;
this.ordinal = ordinal;
构造函数很简单,就是为两个属性赋值。
枚举类型对于我们访问非常的方便和清晰,但是却有很多同学是不建议多多地使用枚举类型,原因就是枚举类型还是比较占用内存的。建议用int类的定义来代替。
一般,我们这样定义枚举。
public enum Test
one,
two,
three
我噻,有人会问了,就这么两行代码,怎么会跟占用内存产生联系呢?
下面我们就从字节码来看看,编译后的class文件有啥东西。
public final class com.mh.myapplication.Test extends java.lang.Enum<com.mh.myapplication.Test>
public static final com.mh.myapplication.Test one;
public static final com.mh.myapplication.Test two;
public static final com.mh.myapplication.Test three;
public static com.mh.myapplication.Test[] values();
Code:
0: getstatic #1 // Field $VALUES:[Lcom/mh/myapplication/Test;
3: invokevirtual #2 // Method "[Lcom/mh/myapplication/Test;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lcom/mh/myapplication/Test;"
9: areturn
public static com.mh.myapplication.Test valueOf(java.lang.String);
Code:
0: ldc #4 // class com/mh/myapplication/Test
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/mh/myapplication/Test
9: areturn
static ;
Code:
0: new #4 // class com/mh/myapplication/Test
3: dup
4: ldc #7 // String one
6: iconst_0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field one:Lcom/mh/myapplication/Test;
13: new #4 // class com/mh/myapplication/Test
16: dup
17: ldc #10 // String two
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field two:Lcom/mh/myapplication/Test;
26: new #4 // class com/mh/myapplication/Test
29: dup
30: ldc #12 // String three
32: iconst_2
33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #13 // Field three:Lcom/mh/myapplication/Test;
39: iconst_3
40: anewarray #4 // class com/mh/myapplication/Test
43: dup
44: iconst_0
45: getstatic #9 // Field one:Lcom/mh/myapplication/Test;
48: aastore
49: dup
50: iconst_1
51: getstatic #11 // Field two:Lcom/mh/myapplication/Test;
54: aastore
55: dup
56: iconst_2
57: getstatic #13 // Field three:Lcom/mh/myapplication/Test;
60: aastore
61: putstatic #1 // Field $VALUES:[Lcom/mh/myapplication/Test;
64: return
上面就是Test枚举类型的字节码,我们来一块一块看看。
首先,会定义一个类Test,继承自Enum,这个没毛病,枚举类型吗。final的。
然后就是,我们定义的one,two,three三个枚举属性,在这里也看到了,分别定义为Test类型。
接下来我们看static这段,这是初始化的工作,大概的流程就是调用Enum的构造方法,创建数组,保存三个枚举值,具体看注释吧。
static ;
Code:
0: new #4 // class com/mh/myapplication/Test
3: dup
//入栈字面字符串值 “one”
4: ldc #7 // String one
//默认值从0开始,入栈数值 0
6: iconst_0
//调用Enum的构造方法,传入“one”和0
7: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #9 // Field one:Lcom/mh/myapplication/Test;
13: new #4 // class com/mh/myapplication/Test
16: dup
//同 one 操作,产生第二个
17: ldc #10 // String two
19: iconst_1
20: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
23: putstatic #11 // Field two:Lcom/mh/myapplication/Test;
26: new #4 // class com/mh/myapplication/Test
29: dup
//产生第三个
30: ldc #12 // String three
32: iconst_2
33: invokespecial #8 // Method "<init>":(Ljava/lang/String;I)V
36: putstatic #13 // Field three:Lcom/mh/myapplication/Test;
39: iconst_3
//创建一个数组
40: anewarray #4 // class com/mh/myapplication/Test
43: dup
44: iconst_0
45: getstatic #9 // Field one:Lcom/mh/myapplication/Test;
取出第一个,并存入数组
48: aastore
49: dup
50: iconst_1
51: getstatic #11 // Field two:Lcom/mh/myapplication/Test;
//取出第二个,并存入数组
54: aastore
55: dup
56: iconst_2
57: getstatic #13 // Field three:Lcom/mh/myapplication/Test;
//取出第三个,并存入数组
60: aastore
//为数组赋值
61: putstatic #1 // Field $VALUES:[Lcom/mh/myapplication/Test;
64: return
还有两个方法调用。
//返回所有枚举值
public static com.mh.myapplication.Test[] values();
Code:
//取出数组值,#1,就是static中第61行那个数组
0: getstatic #1 // Field $VALUES:[Lcom/mh/myapplication/Test;
//调用clone赋值数组
3: invokevirtual #2 // Method "[Lcom/mh/myapplication/Test;".clone:()Ljava/lang/Object;
6: checkcast #3 // class "[Lcom/mh/myapplication/Test;"
9: areturn
//调用Enum的valueOf查找
public static com.mh.myapplication.Test valueOf(java.lang.String);
Code:
0: ldc #4 // class com/mh/myapplication/Test
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/mh/myapplication/Test
9: areturn
从以上字节码我们看到,它会为每个枚举值创建一个对象,保留名字和对应的值,还要创建一个数组来保存所有的枚举,相比一个int类型的数值来说,确实比较占用内存。
小范围使用,感觉还是可以的,如果是高频并且海量定义的话还是可以优化一下的。
以上是关于Java:从字节码看Enum类型的主要内容,如果未能解决你的问题,请参考以下文章
从字节码看Java中for-each循环(增强for循环)实现原理
Java:通过字节码看if-else和switch-case
[jvm解析系列][十三]字节码指令小节,从字节码看JVM的栈解释器执行过程。