首页 > 代码库 > 用javap命令反编译来分析字符串问题

用javap命令反编译来分析字符串问题

编写Test.java,编译完后生成Test.class文件,然后对该文件执行javap -c Test命令,生成字节码指令,阅读并得出结论
一、s1和s2指向常量池的不同常量
①java代码
public class Test {
    public static void main(String[] args) throws IOException {
        String s1="t";
        String s2="m";
    }
}
②反编译可以看到字节码指令执行如下:
Compiled from "Test.java"
public class typ.Test extends java.lang.Object{
public typ.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8//Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.io.IOException;
  Code:
   0:   ldc     #19//String t 进入操作数栈顶
   2:   astore_1     //String t 出操作数栈,赋值给变量1,即s1
   3:   ldc     #21//String m 进入操作数栈顶
   5:   astore_2     //String m 出操作数栈,赋值给变量2,即s2
   6:   return
}
从上面可以看出,两次ldc入栈操作分别指向的是常量#19和#21

二、s1和s2指向常量池的相同常量
①java代码
public class Test {
    public static void main(String[] args) throws IOException {
        String s1="t";
        String s2="t";
    }
}
②反编译可以看到字节码指令执行如下:
Compiled from "Test.java"
public class typ.Test extends java.lang.Object{
public typ.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8//Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.io.IOException;
  Code:
   0:   ldc     #19//String t 进入操作数栈顶
   2:   astore_1     //String t 出操作数栈,赋值给变量1,即s1
   3:   ldc     #19//String t 进入操作数栈顶
   5:   astore_2     //String t 出操作数栈,赋值给变量2,即s2
   6:   return
}
从上面可以看出,两次ldc入栈的操作数都是将指向的常量#19,所以可以看出是s1和s2都是指向常量池的同一个常量“t”

三、进一步看下面
①java代码
public class Test {
    public static void main(String[] args) throws IOException {
        String s1="tm";
        String s2="t"+"m";
    }
}
反编译可以看到字节码指令执行如下:
Compiled from "Test.java"
public class typ.Test extends java.lang.Object{
public typ.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8//Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.io.IOException;
  Code:
   0:   ldc     #19//String tm 进入操作数栈顶
   2:   astore_1     //String tm 出操作数栈,赋值给变量1,即s1
   3:   ldc     #19//String tm 进入操作数栈顶
   5:   astore_2     //String tm 出操作数栈,赋值给变量2,即s2
   6:   return
}
可以看出,两次ldc(load命令, 将操作数入栈)入栈的操作数都是将指向的常量#19即“tm”,说明在编译期间,“t”+"m"就已经变为“tm”这一个常量了

四、再进一步
①java代码
public class Test {
    public static void main(String[] args) throws IOException {
        String tmp = "t";
        String s1 = "m";
        String s2 = tmp + "m";
    }
}
反编译可以看到字节码指令执行如下:
Compiled from "Test.java"
public class typ.Test extends java.lang.Object{
public typ.Test();
  Code:
   0:   aload_0
   1:   invokespecial   #8//Method java/lang/Object."<init>":()V
   4:   return

public static void main(java.lang.String[])   throws java.io.IOException;
  Code:
   0:   ldc     #19//String t 进入操作数栈顶
   2:   astore_1     //String t 出操作数栈,赋值给变量1,即tmp
   3:   ldc     #21//String m 进入操作数栈顶
   5:   astore_2     //String m 出操作数栈,赋值给变量2,即s1
   6:   new     #23//class java/lang/StringBuilder 创建StringBuilder类型对
 
   9:   dup          //复制栈顶一个字长的数据,将复制后的数据压栈 
   10:  aload_1      //tmp 进入操作数栈顶
   11:  invokestatic    #25//Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
   14:  invokespecial   #31//Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
   17:  ldc     #21//String m
   19:  invokevirtual   #34//Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   22:  invokevirtual   #38//Method java/lang/StringBuilder.toString:()Ljava/lang/String;
   25:  astore_3
   26:  return
}

从上面的代码可以看出,string的相加要通过stringbuilder,并且由于tmp是变量,所以调用的invokevirtual指令(在编译器无法确定),这说明不是
在编译期完成,因此s1与s2时不相等的