zoukankan      html  css  js  c++  java
  • java7(1)——反编译深入理解增强的switch(读字节命令实战)

    【本文介绍】

      本文主要讲java_7 的改进switch的底层实现。反编译一个使用带String的switch的demo并一步步解析反编译出来的字节命令,从编译的角度解读switch的底层实现。

      

    【正文】

      在java7中,switch()可以放进去String 类型了,这无非是一大便利。底层JVM的swtich并没有真正的改进,只是在编译阶段,编译器把关于String的switch拆分成if语句而已。

      我们写一个简单的例子测试一下:

    (1)Test类:switch()使用String

     1 public class Test {
     2 
     3     public  void test(String str) {
     4         
     5         switch(str){
     6         
     7         case "a": System.out.println("a");break;
     8         case "b": System.out.println("b");break;
     9         default : System.out.println("default");
    10         
    11         }
    12     }
    13 }
    View Code

    (2)Test2类:switch()使用int

    public class Test2 {
    
        public  void test(int str) {
            
            switch(str){
            
            case 1: System.out.println("1");break;
            case 2: System.out.println("2");break;
            default : System.out.println("default");
            
            }
        }
    }
    View Code

    javac 编译 , javap -c 反编译 Test 后的结果:

    public class Test {
      public Test();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":
    ()V
           4: return
    
      public void test(java.lang.String);
        Code:
           0: aload_1
           1: astore_2                ---------------从这里开始------------
           2: iconst_m1              // 将int型-1推送至栈顶
           3: istore_3               // 赋值,因为此时栈顶元素为-1,所以赋值-1
           4: aload_2
           5: invokevirtual #2                  // Method java/lang/String.hashCode: 调用hasCode方法
    ()I
           8: lookupswitch  { // 2        源码本来只有一次switch,现在被拆分成两次,这是第一次switch,下面还有一次公共的
                        97: 36           case 97 : 跳至36行 aload_2
                        98: 50           case 98 :跳至50行 aload_2
                   default: 61           default : 跳至61行 iload_3
              }
          36: aload_2
          37: ldc           #3                  // String a  下面equal的内容
          39: invokevirtual #4                  // Method java/lang/String.equals:(L  进行equal的比较
    java/lang/Object;)Z
          42: ifeq          61           // if 语句
          45: iconst_0               // 将int型0推送至栈顶
          46: istore_3               // 赋值,因为此时栈顶元素为 0 ,所以赋值0
          47: goto          61
          50: aload_2
          51: ldc           #5                  // String b   下面equal的内容
          53: invokevirtual #4                  // Method java/lang/String.equals:(L  进行equal的比较
    java/lang/Object;)Z
          56: ifeq          61           // if 语句
          59: iconst_1               // 将int型1推送至栈顶 
          60: istore_3               // 赋值,因为此时栈顶元素为 1 , 所以赋值1
          61: iload_3                ----------------到这里结束---------------
          62: lookupswitch  { // 2
                         0: 88
                         1: 99
                   default: 110
              }
          88: getstatic     #6                  // Field java/lang/System.out:Ljava/
    io/PrintStream;
          91: ldc           #3                  // String a
          93: invokevirtual #7                  // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
          96: goto          118
          99: getstatic     #6                  // Field java/lang/System.out:Ljava/
    io/PrintStream;
         102: ldc           #5                  // String b
         104: invokevirtual #7                  // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
         107: goto          118
         110: getstatic     #6                  // Field java/lang/System.out:Ljava/
    io/PrintStream;
         113: ldc           #8                  // String default
         115: invokevirtual #7                  // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
         118: return
    }
    View Code

    javac 编译 , javap -c 反编译 Test2 后的结果:

    public class Test2 {
      public Test2();
        Code:
           0: aload_0
           1: invokespecial #1                  // Method java/lang/Object."<init>":
    ()V
           4: return
    
      public void test(int);
        Code:
           0: iload_1
           1: lookupswitch  { // 2
                         1: 28
                         2: 39
                   default: 50
              }
          28: getstatic     #2                  // Field java/lang/System.out:Ljava/
    io/PrintStream;
          31: ldc           #3                  // String 1
          33: invokevirtual #4                  // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
          36: goto          58
          39: getstatic     #2                  // Field java/lang/System.out:Ljava/
    io/PrintStream;
          42: ldc           #5                  // String 2
          44: invokevirtual #4                  // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
          47: goto          58
          50: getstatic     #2                  // Field java/lang/System.out:Ljava/
    io/PrintStream;
          53: ldc           #6                  // String default
          55: invokevirtual #4                  // Method java/io/PrintStream.printl
    n:(Ljava/lang/String;)V
          58: return
    }
    View Code

      大家看到这么多字节码是不是有点头晕不想再看下去了?其实只需稍稍观察比较就能发现”从这里开始“——”到这里结束“中间那些字节码是下面那个字节码文件所没有的,所以我们研究这几行代码就行了。又看我用红色字体标出来的注释,结果就显而易见了:

    (0)用一个int类型变量代表String类型变量

    (1)获取String字符串的hashCode

    (2)case hashCode

    (3)用if语句处理String

    (4)为int类型的变量赋值

    (5)真正的swtich,现在传入的是上面得出的int类型变量。

    把上面的字节码文件翻译成java即:

     1 public class test {
     2 
     3     public void test(String str) {
     4         
     5         int i = -1;
     6         
     7         switch(str.hashCode()){
     8             
     9         case 97:
    10             if(str.equals("a")){
    11                 i = 0;
    12             }
    13             break;
    14         case 98:
    15             if(str.equals("b")){
    16                 break;
    17             }
    18         }
    19         
    20         switch(i) {
    21         
    22         case 0:
    23             System.out.println("a");
    24             break;
    25         
    26         case 1:
    27             System.out.println("b");
    28             break;
    29             
    30         default:
    31             System.out.println("default");
    32         }
    33     }
    34 }
    View Code
  • 相关阅读:
    class的大小
    派生表中第一个基类没有虚函数,派生类存在虚函数时的内存布局
    转: inline关键字使用
    三角形面积计算(海伦公式或叉积绝对值的一半)
    单继承时虚函数表解析
    C语言中的atan和atan2
    n皇后问题的递归和迭代版 leetcode N-Queens
    linux网络编程中INADDR_ANY的含义
    数据库有哪些设计技巧
    原来Github上也有这么多的JavaScript学习资源!
  • 原文地址:https://www.cnblogs.com/xiaoMzjm/p/3885990.html
Copyright © 2011-2022 走看看