JVM-06

作者: 平头哥2 | 来源:发表于2019-06-24 18:03 被阅读0次

    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."<init>":()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               <init>
       #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          // "<init>":()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."<init>":()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<E> getDeclaringClass() 返回与此枚举常量的枚举类型相对应的 Class 对象。
     * 3. String name()  返回此枚举常量的名称,在其枚举声明中对其进行声明。
     * 4. int ordinal()  返回枚举常量的序数(它在枚举声明中的位置,其中初始常量序数为零)。
     * 5. String toString()  返回枚举常量的名称,它包含在声明中。
     * 6. static <T extends Enum<T>> T valueOf(Class<T> enumType, String name) 返回带指定名称的指定枚举类型的枚举常量。
     * @author zishi.ghq
     *
     */
    public enum EnumTest {
        MON, TUE, WED, THU, FRI, SAT, SUN;
        //这段代码实际上调用了7次 Enum(String name, int ordinal):
        // new Enum<EnumTest>("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<EnumTest>
      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."<init>":(Ljava/lang/String;I)V
       #7 = String             #23            // MON
       #8 = Methodref          #4.#56         // EnumTest."<init>":(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               <init>
      #43 = Utf8               (Ljava/lang/String;I)V
      #44 = Utf8               this
      #45 = Utf8               Signature
      #46 = Utf8               ()V
      #47 = Utf8               <clinit>
      #48 = Utf8               Ljava/lang/Enum<LEnumTest;>;
      #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        // "<init>":(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 "<init>":(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 "<init>":(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 "<init>":(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 "<init>":(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 "<init>":(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 "<init>":(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 "<init>":(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<LEnumTest;>;
    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."<init>":()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

          本文链接:https://www.haomeiwen.com/subject/foaoqctx.html