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:从字节码看Enum类型

从字节码看Java中for-each循环(增强for循环)实现原理

Java:通过字节码看if-else和switch-case

[jvm解析系列][十三]字节码指令小节,从字节码看JVM的栈解释器执行过程。

[jvm解析系列][十三]字节码指令小节,从字节码看JVM的栈解释器执行过程。

[jvm解析系列][十三]字节码指令小节,从字节码看JVM的栈解释器执行过程。