JVM-06

switch-case的字节码指令:

Java代码如下:

public class HelloWorld{

    public int testSwitchCase(int a) {
        int b = -23;
        switch(a){
            case 5:
                System.out.println(5);
                b = 1;
            case 2:
                System.out.println(2);
                b = 2;
                break;
            case 1:
                System.out.println(1);
                b = 5;
            case 6:
                System.out.println(6);
                b = 6;
            default: 
                System.out.println("default");
                b = 0;
        }
        return b;
    }
    
    public int testSwitchCase02(int a) {
        
        switch(a){
            case 100:
                return 1;
            case 2000:
                return 2;
            case 1:
                return 5;
            case 20:
                return 6;
            default: 
                return 0;
        }
    }
}

字节码指令如下:

$ javap -v HelloWorld.class
Classfile /D:/ideaproject/jvm/HelloWorld.class
  Last modified 2019-6-24; size 883 bytes
  MD5 checksum 0d1346a9766d2495fdad312ece7fbfc6
  Compiled from "HelloWorld.java"
public class HelloWorld
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #7.#24         // java/lang/Object."":()V
   #2 = Fieldref           #25.#26        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V
   #4 = String             #29            // default
   #5 = Methodref          #27.#30        // java/io/PrintStream.println:(Ljava/lang/String;)V
   #6 = Class              #31            // HelloWorld
   #7 = Class              #32            // java/lang/Object
   #8 = Utf8               
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               LocalVariableTable
  #13 = Utf8               this
  #14 = Utf8               LHelloWorld;
  #15 = Utf8               testSwitchCase
  #16 = Utf8               (I)I
  #17 = Utf8               a
  #18 = Utf8               I
  #19 = Utf8               b
  #20 = Utf8               StackMapTable
  #21 = Utf8               testSwitchCase02
  #22 = Utf8               SourceFile
  #23 = Utf8               HelloWorld.java
  #24 = NameAndType        #8:#9          // "":()V
  #25 = Class              #33            // java/lang/System
  #26 = NameAndType        #34:#35        // out:Ljava/io/PrintStream;
  #27 = Class              #36            // java/io/PrintStream
  #28 = NameAndType        #37:#38        // println:(I)V
  #29 = Utf8               default
  #30 = NameAndType        #37:#39        // println:(Ljava/lang/String;)V
  #31 = Utf8               HelloWorld
  #32 = Utf8               java/lang/Object
  #33 = Utf8               java/lang/System
  #34 = Utf8               out
  #35 = Utf8               Ljava/io/PrintStream;
  #36 = Utf8               java/io/PrintStream
  #37 = Utf8               println
  #38 = Utf8               (I)V
  #39 = Utf8               (Ljava/lang/String;)V
{
  public HelloWorld();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."":()V
         4: return
      LineNumberTable:
        line 1: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LHelloWorld;

  public int testSwitchCase(int);
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=3, args_size=2
         0: bipush        -23
         2: istore_2
         3: iload_1
         4: tableswitch   { // 1 to 6
                       1: 65
                       2: 53
                       3: 85
                       4: 85
                       5: 44
                       6: 74
                 default: 85
            }
        44: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        47: iconst_5
        48: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        51: iconst_1
        52: istore_2
        53: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        56: iconst_2
        57: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        60: iconst_2
        61: istore_2
        62: goto          95
        65: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        68: iconst_1
        69: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        72: iconst_5
        73: istore_2
        74: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        77: bipush        6
        79: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
        82: bipush        6
        84: istore_2
        85: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        88: ldc           #4                  // String default
        90: invokevirtual #5                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        93: iconst_0
        94: istore_2
        95: iload_2
        96: ireturn
      LineNumberTable:
        line 4: 0
        line 5: 3
        line 7: 44
        line 8: 51
        line 10: 53
        line 11: 60
        line 12: 62
        line 14: 65
        line 15: 72
        line 17: 74
        line 18: 82
        line 20: 85
        line 21: 93
        line 23: 95
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      97     0  this   LHelloWorld;
            0      97     1     a   I
            3      94     2     b   I
      StackMapTable: number_of_entries = 6
        frame_type = 252 /* append */
          offset_delta = 44
          locals = [ int ]
        frame_type = 8 /* same */
        frame_type = 11 /* same */
        frame_type = 8 /* same */
        frame_type = 10 /* same */
        frame_type = 9 /* same */

  public int testSwitchCase02(int);
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=2, args_size=2
         0: iload_1
         1: lookupswitch  { // 4
                       1: 48
                      20: 50
                     100: 44
                    2000: 46
                 default: 53
            }
        44: iconst_1
        45: ireturn
        46: iconst_2
        47: ireturn
        48: iconst_5
        49: ireturn
        50: bipush        6
        52: ireturn
        53: iconst_0
        54: ireturn
      LineNumberTable:
        line 28: 0
        line 30: 44
        line 32: 46
        line 34: 48
        line 36: 50
        line 38: 53
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      55     0  this   LHelloWorld;
            0      55     1     a   I
      StackMapTable: number_of_entries = 5
        frame_type = 44 /* same */
        frame_type = 1 /* same */
        frame_type = 1 /* same */
        frame_type = 1 /* same */
        frame_type = 2 /* same */
}
SourceFile: "HelloWorld.java"

结论是:switch-case 语句 在 case 比较稀疏的情况下,编译器会使用 lookupswitch 指令来实现,反之,编译器会使用 tableswitch 来实现

枚举复习:

/**
 * enum 对象的常用方法介绍:
 * 1. int compareTo(E o)  比较此枚举与指定对象的顺序。
 * 2. Class getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象。
 * 3. String name()  返回此枚举常量的名称,在其枚举声明中对其进行声明。
 * 4. int ordinal()  返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
 * 5. String toString()  返回枚举常量的名称,它包含在声明中。
 * 6. static > T valueOf(Class enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。
 * @author zishi.ghq
 *
 */
public enum EnumTest {
    MON, TUE, WED, THU, FRI, SAT, SUN;
    //这段代码实际上调用了7次 Enum(String name, int ordinal):
    // new Enum("MON",0);
    public static void main(String[] args) {
        
        EnumTest[] values = EnumTest.values();
        for (EnumTest enumTest : values) {
            System.out.println(enumTest.name());
            System.out.println(enumTest.ordinal());
        }
        
        System.out.println("---------------------------------");
        EnumTest test = EnumTest.MON;
        
        switch (test) {
        case MON:
            System.out.println("星期一");
            break;
        default:
            System.out.println("不是星期一");
            break;
        }
        System.out.println("---------------------------------");
        
        System.out.println(test.compareTo(FRI));
        //getDeclaringClass()
        System.out.println(test.getDeclaringClass().getName());//jvm.EnumTest
        //toString()
        System.out.println(test.toString());//MON
        //ordinal(), 返回值是从 0 开始
        System.out.println(test.ordinal());
    }
}

枚举

public enum EnumTest {
    MON, TUE, WED, THU, FRI, SAT, SUN;
}

汇编结果:

$ javap -v EnumTest.class
Classfile /D:/ideaproject/jvm/EnumTest.class
  Last modified 2019-6-24; size 1089 bytes
  MD5 checksum 43371fe799ea9f40ce42b722a4810a53
  Compiled from "EnumTest.java"
public final class EnumTest extends java.lang.Enum
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM
Constant pool:
   #1 = Fieldref           #4.#51         // EnumTest.$VALUES:[LEnumTest;
   #2 = Methodref          #52.#53        // "[LEnumTest;".clone:()Ljava/lang/Object;
   #3 = Class              #32            // "[LEnumTest;"
   #4 = Class              #54            // EnumTest
   #5 = Methodref          #22.#55        // java/lang/Enum.valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
   #6 = Methodref          #22.#56        // java/lang/Enum."":(Ljava/lang/String;I)V
   #7 = String             #23            // MON
   #8 = Methodref          #4.#56         // EnumTest."":(Ljava/lang/String;I)V
   #9 = Fieldref           #4.#57         // EnumTest.MON:LEnumTest;
  #10 = String             #25            // TUE
  #11 = Fieldref           #4.#58         // EnumTest.TUE:LEnumTest;
  #12 = String             #26            // WED
  #13 = Fieldref           #4.#59         // EnumTest.WED:LEnumTest;
  #14 = String             #27            // THU
  #15 = Fieldref           #4.#60         // EnumTest.THU:LEnumTest;
  #16 = String             #28            // FRI
  #17 = Fieldref           #4.#61         // EnumTest.FRI:LEnumTest;
  #18 = String             #29            // SAT
  #19 = Fieldref           #4.#62         // EnumTest.SAT:LEnumTest;
  #20 = String             #30            // SUN
  #21 = Fieldref           #4.#63         // EnumTest.SUN:LEnumTest;
  #22 = Class              #64            // java/lang/Enum
  #23 = Utf8               MON
  #24 = Utf8               LEnumTest;
  #25 = Utf8               TUE
  #26 = Utf8               WED
  #27 = Utf8               THU
  #28 = Utf8               FRI
  #29 = Utf8               SAT
  #30 = Utf8               SUN
  #31 = Utf8               $VALUES
  #32 = Utf8               [LEnumTest;
  #33 = Utf8               values
  #34 = Utf8               ()[LEnumTest;
  #35 = Utf8               Code
  #36 = Utf8               LineNumberTable
  #37 = Utf8               valueOf
  #38 = Utf8               (Ljava/lang/String;)LEnumTest;
  #39 = Utf8               LocalVariableTable
  #40 = Utf8               name
  #41 = Utf8               Ljava/lang/String;
  #42 = Utf8               
  #43 = Utf8               (Ljava/lang/String;I)V
  #44 = Utf8               this
  #45 = Utf8               Signature
  #46 = Utf8               ()V
  #47 = Utf8               
  #48 = Utf8               Ljava/lang/Enum;
  #49 = Utf8               SourceFile
  #50 = Utf8               EnumTest.java
  #51 = NameAndType        #31:#32        // $VALUES:[LEnumTest;
  #52 = Class              #32            // "[LEnumTest;"
  #53 = NameAndType        #65:#66        // clone:()Ljava/lang/Object;
  #54 = Utf8               EnumTest
  #55 = NameAndType        #37:#67        // valueOf:(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
  #56 = NameAndType        #42:#43        // "":(Ljava/lang/String;I)V
  #57 = NameAndType        #23:#24        // MON:LEnumTest;
  #58 = NameAndType        #25:#24        // TUE:LEnumTest;
  #59 = NameAndType        #26:#24        // WED:LEnumTest;
  #60 = NameAndType        #27:#24        // THU:LEnumTest;
  #61 = NameAndType        #28:#24        // FRI:LEnumTest;
  #62 = NameAndType        #29:#24        // SAT:LEnumTest;
  #63 = NameAndType        #30:#24        // SUN:LEnumTest;
  #64 = Utf8               java/lang/Enum
  #65 = Utf8               clone
  #66 = Utf8               ()Ljava/lang/Object;
  #67 = Utf8               (Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;
{
  public static final EnumTest MON;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final EnumTest TUE;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final EnumTest WED;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final EnumTest THU;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final EnumTest FRI;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final EnumTest SAT;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

  public static final EnumTest SUN;
    descriptor: LEnumTest;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL, ACC_ENUM

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

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

  static {};
    descriptor: ()V
    flags: ACC_STATIC
    Code:
      stack=4, locals=0, args_size=0
         0: new           #4                  // class EnumTest
         3: dup
         4: ldc           #7                  // String MON
         6: iconst_0
         7: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        10: putstatic     #9                  // Field MON:LEnumTest;
        13: new           #4                  // class EnumTest
        16: dup
        17: ldc           #10                 // String TUE
        19: iconst_1
        20: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        23: putstatic     #11                 // Field TUE:LEnumTest;
        26: new           #4                  // class EnumTest
        29: dup
        30: ldc           #12                 // String WED
        32: iconst_2
        33: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        36: putstatic     #13                 // Field WED:LEnumTest;
        39: new           #4                  // class EnumTest
        42: dup
        43: ldc           #14                 // String THU
        45: iconst_3
        46: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        49: putstatic     #15                 // Field THU:LEnumTest;
        52: new           #4                  // class EnumTest
        55: dup
        56: ldc           #16                 // String FRI
        58: iconst_4
        59: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        62: putstatic     #17                 // Field FRI:LEnumTest;
        65: new           #4                  // class EnumTest
        68: dup
        69: ldc           #18                 // String SAT
        71: iconst_5
        72: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        75: putstatic     #19                 // Field SAT:LEnumTest;
        78: new           #4                  // class EnumTest
        81: dup
        82: ldc           #20                 // String SUN
        84: bipush        6
        86: invokespecial #8                  // Method "":(Ljava/lang/String;I)V
        89: putstatic     #21                 // Field SUN:LEnumTest;
        92: bipush        7
        94: anewarray     #4                  // class EnumTest
        97: dup
        98: iconst_0
        99: getstatic     #9                  // Field MON:LEnumTest;
       102: aastore
       103: dup
       104: iconst_1
       105: getstatic     #11                 // Field TUE:LEnumTest;
       108: aastore
       109: dup
       110: iconst_2
       111: getstatic     #13                 // Field WED:LEnumTest;
       114: aastore
       115: dup
       116: iconst_3
       117: getstatic     #15                 // Field THU:LEnumTest;
       120: aastore
       121: dup
       122: iconst_4
       123: getstatic     #17                 // Field FRI:LEnumTest;
       126: aastore
       127: dup
       128: iconst_5
       129: getstatic     #19                 // Field SAT:LEnumTest;
       132: aastore
       133: dup
       134: bipush        6
       136: getstatic     #21                 // Field SUN:LEnumTest;
       139: aastore
       140: putstatic     #1                  // Field $VALUES:[LEnumTest;
       143: return
      LineNumberTable:
        line 2: 0
        line 1: 92
}
Signature: #48                          // Ljava/lang/Enum;
SourceFile: "EnumTest.java"

反编译查看源码:

public final class EnumTest extends Enum
{

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

    public static EnumTest valueOf(String name)//根据名字返回枚举常量
    {
        return (EnumTest)Enum.valueOf(EnumTest, name);
    }

    private EnumTest(String s, int i) //构造方法私有化
    {
        super(s, i);
    }

    public static final EnumTest MON;//静态常量
    public static final EnumTest TUE;
    public static final EnumTest WED;
    public static final EnumTest THU;
    public static final EnumTest FRI;
    public static final EnumTest SAT;
    public static final EnumTest SUN;
    private static final EnumTest $VALUES[];

    static 
    {
        MON = new EnumTest("MON", 0);
        TUE = new EnumTest("TUE", 1);
        WED = new EnumTest("WED", 2);
        THU = new EnumTest("THU", 3);
        FRI = new EnumTest("FRI", 4);
        SAT = new EnumTest("SAT", 5);
        SUN = new EnumTest("SUN", 6);
        $VALUES = (new EnumTest[] {
            MON, TUE, WED, THU, FRI, SAT, SUN
        });
    }
}

1. 枚举类其实就是一个普通的java类,默认继承了Enum。

2. 枚举类MON就相当于创建了一个类的实例,静态的

枚举实现switch-case:

public static void main(String[] args) {
        WeekEnum test = WeekEnum.FRI;
        
        switch (test) {
        case MON:
            System.out.println("星期一");
            break;
        case TUE:
            System.out.println("星期二");
            break;
        case WED:
            System.out.println("星期三");
            break;
        case THU:
            System.out.println("星期四");
            break;
        case SAT:
            System.out.println("星期五");
            break;
        case FRI:
            System.out.println("星期六");
            break;
        default:
            System.out.println("星期日");
            break;
        }
}

汇编指令

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: getstatic     #2                  // Field WeekEnum.FRI:LWeekEnum;
       3: astore_1
       4: getstatic     #3                  // Field Test$1.$SwitchMap$WeekEnum:[I
       7: aload_1
       8: invokevirtual #4                  // Method WeekEnum.ordinal:()I
      11: iaload
      12: lookupswitch  { // 2
                     1: 40
                     2: 51
               default: 62
          }
      40: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      43: ldc           #6                  // String 1
      45: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      48: goto          70
      51: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      54: ldc           #8                  // String 2
      56: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      59: goto          70
      62: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;
      65: ldc           #9                  // String sleep
      67: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      70: return
}

你可能感兴趣的:(JVM-06)