字節碼層面理解java枚舉Enum

1.自定義一個枚舉類型Color.java

public enum Color {
    RED,BLUE,YELLOW
}

2.javac編譯Color.java,生成Color.class。javac的時候記得添加-g選項纔會生成局部變量表。

javac -g Color.java

3.javap反編譯Color.class,重定向到Color.txt

-v 輸出詳細信息

-p 不設置-p則不輸出private類型變量和方法以及內部類的信息

javap -v -p Color.class > Color.txt

4.Color.txt內容如下

Classfile /C:/workspace/test/src/main/java/Color.class
  Last modified 2019-12-13; size 885 bytes
  MD5 checksum 5eb4ad1bb3162939cfda9b83c768e64d
  Compiled from "Color.java"
public final class Color extends java.lang.Enum<Color>
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref           #4.#39         // Color.$VALUES:[LColor;
   #2 = Methodref          #40.#41        // "[LColor;".clone:()Ljava/lang/Object;
   #3 = Class              #20            // "[LColor;"
   #4 = Class              #42            // Color
   #5 = Methodref          #14.#43        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #14.#44        // java/lang/Enum."<init>":(Ljava/lang/String;I)V
   #7 = String             #15            // RED
   #8 = Methodref          #4.#44         // Color."<init>":(Ljava/lang/String;I)V
   #9 = Fieldref           #4.#45         // Color.RED:LColor;
  #10 = String             #17            // BLUE
  #11 = Fieldref           #4.#46         // Color.BLUE:LColor;
  #12 = String             #18            // YELLOW
  #13 = Fieldref           #4.#47         // Color.YELLOW:LColor;
  #14 = Class              #48            // java/lang/Enum
  #15 = Utf8               RED
  #16 = Utf8               LColor;
  #17 = Utf8               BLUE
  #18 = Utf8               YELLOW
  #19 = Utf8               $VALUES
  #20 = Utf8               [LColor;
  #21 = Utf8               values
  #22 = Utf8               ()[LColor;
  #23 = Utf8               Code
  #24 = Utf8               LineNumberTable
  #25 = Utf8               valueOf
  #26 = Utf8               (Ljava/lang/String;)LColor;
  #27 = Utf8               LocalVariableTable
  #28 = Utf8               name
  #29 = Utf8               Ljava/lang/String;
  #30 = Utf8               <init>
  #31 = Utf8               (Ljava/lang/String;I)V
  #32 = Utf8               this
  #33 = Utf8               Signature
  #34 = Utf8               ()V
  #35 = Utf8               <clinit>
  #36 = Utf8               Ljava/lang/Enum<LColor;>;
  #37 = Utf8               SourceFile
  #38 = Utf8               Color.java
  #39 = NameAndType        #19:#20        // $VALUES:[LColor;
  #40 = Class              #20            // "[LColor;"
  #41 = NameAndType        #49:#50        // clone:()Ljava/lang/Object;
  #42 = Utf8               Color
  #43 = NameAndType        #25:#51        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #44 = NameAndType        #30:#31        // "<init>":(Ljava/lang/String;I)V
  #45 = NameAndType        #15:#16        // RED:LColor;
  #46 = NameAndType        #17:#16        // BLUE:LColor;
  #47 = NameAndType        #18:#16        // YELLOW:LColor;
  #48 = Utf8               java/lang/Enum
  #49 = Utf8               clone
  #50 = Utf8               ()Ljava/lang/Object;
  #51 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
  public static final Color RED;
    descriptor: LColor;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final Color BLUE;
    descriptor: LColor;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final Color YELLOW;
    descriptor: LColor;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  private static final Color[] $VALUES;
    descriptor: [LColor;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

  public static Color[] values();
    descriptor: ()[LColor;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[LColor;
         3: invokevirtual #2                  // Method "[LColor;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[LColor;"
         9: areturn
      LineNumberTable:
        line 5: 0

  public static Color valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)LColor;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class Color
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class Color
         9: areturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      10     0  name   Ljava/lang/String;

  private Color();
    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 #6                  // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
         6: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       7     0  this   LColor;
    Signature: #34                          // ()V

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class Color
         3: dup
         4: ldc           #7                  // String RED
         6: iconst_0
         7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        10: putstatic     #9                  // Field RED:LColor;
        13: new           #4                  // class Color
        16: dup
        17: ldc           #10                 // String BLUE
        19: iconst_1
        20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        23: putstatic     #11                 // Field BLUE:LColor;
        26: new           #4                  // class Color
        29: dup
        30: ldc           #12                 // String YELLOW
        32: iconst_2
        33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        36: putstatic     #13                 // Field YELLOW:LColor;
        39: iconst_3
        40: anewarray     #4                  // class Color
        43: dup
        44: iconst_0
        45: getstatic     #9                  // Field RED:LColor;
        48: aastore
        49: dup
        50: iconst_1
        51: getstatic     #11                 // Field BLUE:LColor;
        54: aastore
        55: dup
        56: iconst_2
        57: getstatic     #13                 // Field YELLOW:LColor;
        60: aastore
        61: putstatic     #1                  // Field $VALUES:[LColor;
        64: return
      LineNumberTable:
        line 6: 0
        line 5: 39
}
Signature: #36                          // Ljava/lang/Enum<LColor;>;
SourceFile: "Color.java"

 

5. 手動反編譯的代碼如下

public final class Color extends Enum<Color> {

    private static final Color RED;
    private static final Color BLUE;
    private static final Color YELLOW;

    private static final Color[] $VALUES;

    static {
        RED = new Color("RED", 0);
        BLUE = new Color("BLUE", 1);
        YELLOW = new Color("YELLOW", 2);
        $VALUES = new Color[3];
        $VALUES[0] = RED;
        $VALUES[1] = BLUE;
        $VALUES[2] = YELLOW;
    }


    private Color(String name, int ordinal) {
        super(name, ordinal);
    }

    public static Color[] values(){
        return $VALUES.clone();
    }

    public static Color valueOf(String name){
        return Enum.valueOf(Color.class, name);
    }

}

編譯器不允許直接繼承Enum,所以會提示Classes cannot directly extend 'java.lang.Enum'

 

6.下面對Color類的字節碼進行分析,如果不感興趣,或者對字節碼不懂的,可以不往下看。

6.1 類信息

大括號前面的,是Color類的一些摘要信息。

public final class Color extends java.lang.Enum<Color>
表明enum Color.java在編譯後生成了一個類Color,且Color繼承了java.lang.Enum<Color>,該類是final類型,所以枚舉類都是不可以繼承的。

 

大括號裏面,包含了Color類編譯後的字段和方法,下面我們來分析Color類編譯後生成了哪些字段和方法

 

6.2 枚舉變量

  public static final Color RED;
    descriptor: LColor;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final Color BLUE;
    descriptor: LColor;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final Color YELLOW;
    descriptor: LColor;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

可以看到 RED,BLUE, YELLOW 枚舉變量,在編譯後就是Color類的一個實例。

 

6.3 values()方法

  private static final Color[] $VALUES;
    descriptor: [LColor;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL, ACC_SYNTHETIC

  public static Color[] values();
    descriptor: ()[LColor;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=1, locals=0, args_size=0
         0: getstatic     #1                  // Field $VALUES:[LColor;
         3: invokevirtual #2                  // Method "[LColor;".clone:()Ljava/lang/Object;
         6: checkcast     #3                  // class "[LColor;"
         9: areturn
      LineNumberTable:
        line 5: 0

編譯後,Color中生成了一個私有的$VALUES變量,存儲了所以了Color變量(RED,BLUE,YELLOW),且變量順序和聲明順序一樣。自動生成的values()方法會返回$VALUES。

 

6.4 valueOf()方法

public static Color valueOf(java.lang.String);
    descriptor: (Ljava/lang/String;)LColor;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: ldc           #4                  // class Color
         2: aload_0
         3: invokestatic  #5                  // Method java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
         6: checkcast     #4                  // class Color
         9: areturn

生成的valueOf(String)方法,會調用Enum.valueOf(Class,String)方法返回同名Color實例。

 

6.5 構造方法

private Color();
    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 #6                  // Method java/lang/Enum."<init>":(Ljava/lang/String;I)V
         6: return

枚舉的構造方法都是私有的,是不能在外部調用。

 

6.6 static靜態塊

static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class Color
         3: dup
         4: ldc           #7                  // String RED
         6: iconst_0
         7: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        10: putstatic     #9                  // Field RED:LColor;
        13: new           #4                  // class Color
        16: dup
        17: ldc           #10                 // String BLUE
        19: iconst_1
        20: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        23: putstatic     #11                 // Field BLUE:LColor;
        26: new           #4                  // class Color
        29: dup
        30: ldc           #12                 // String YELLOW
        32: iconst_2
        33: invokespecial #8                  // Method "<init>":(Ljava/lang/String;I)V
        36: putstatic     #13                 // Field YELLOW:LColor;
        39: iconst_3
        40: anewarray     #4                  // class Color
        43: dup
        44: iconst_0
        45: getstatic     #9                  // Field RED:LColor;
        48: aastore
        49: dup
        50: iconst_1
        51: getstatic     #11                 // Field BLUE:LColor;
        54: aastore
        55: dup
        56: iconst_2
        57: getstatic     #13                 // Field YELLOW:LColor;
        60: aastore
        61: putstatic     #1                  // Field $VALUES:[LColor;
        64: return

在靜態塊中,創建了所有枚舉變量的實例,以及$VALUES 數組。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章