从字节码分析字符串是否相等
Posted 364.99°
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从字节码分析字符串是否相等相关的知识,希望对你有一定的参考价值。
1. 简单分析创建字符串的虚拟机指令
1. 字节码的简单阅读
先看一段简单程序及其字节码,了解字节码的简单阅读:javap -c Demo1.class
javap -v xxx.class
反编译,展示字节码的详细信息。
详细信息内容:类的基本信息、常量池、类方法定义、虚拟机指令
如何读字节码:
- 补充说明:# 后面有两个数字,比如说 # 3, 2 ,这是说明 这里调用了常量池的 # 3的值,而 # 3 的值又对应了两个常量符号。
常量池: 给二进制字节码指令(虚拟机指令)提供一些常量符号,根据常量符号表的查找方式去找到目标值。
2. 字符串的创建过程
javap -v TestStringTable.class
- 常量池中的信息,都会被加载到运行时常量池中,这时的 1、2、1还没有变成字符串对象
ldc #2
会把对应的符号变成一个对象astore_1
会将1第个引用类型本地变量推送至栈顶(就是存储到 Slot=1的地方)
经过上述过程,可发现,只有在用到变量的时候,才会创建为java对象。
2. 为什么不相等
a + b != “12”
追加下面的代码,并反编译。
String d = a + b;
读取过程:
-
new
一个StringBuilder
对象
dup,复制栈顶数值并将复制值压入栈顶 -
invokespecial
调用实例方法"<init>":()V
——StringBuilder
的无参构造方法 -
aload_1
调用栈帧slot为1的变量(a)的值 -
invokespecial
调用实例方法append
-
aload_2
调用栈帧slot为2的变量 (b) 的值 -
invokespecial
调用实例方法append
-
invokespecial
调用实例方法toString
-
astore 4
将toString后的值存入栈帧Slot为4的变量(d)
分析:
-
d的字符串对象存在堆里面
-
如果是直接定义一个字符串变量
String e = "12"
那么这个 “12” 是存在常量池中的
-
所以
e == d
结果为 false
“1” + “2” = “12”
追加代码
String e = "1" + "2";
String f = "12";
反编译:
可以发现,两者都是直接从常量池中创建 “12” 对象,然后将对象传给 e 和 f。
所以 e == f
为 true。
为什么JVM对两者的处理不同?
这都是 javac
优化编译过程的结果:
String e = "1" + "2";
直接从常量池取值的原因:javac
在编译期间的优化,其结果在编译的时候,就已经知道了(在编译期间就已经拼接了)- 而
String d = a + b;
,a 和 b 都是变量,值可能会改变,为了保证结果的正确性,就使用 StringBuilder 来进行动态拼接
注意: 可以通过 str.intern()
将动态拼接、存在堆中的字符串对象放入串池
-
如果串池中有该字符串对象,就不会放入
-
如果串池中没有该字符串对象,就会放入,也会将池中的对象返回
String a = "1"; String b = new String("1"); boolean flag1 = a == b;// return false String b1 = b.intern();// 放入串池 boolean flag2 = a == b1;// return true String c = new String("1") + new String("2"); c.intern(); boolean flag1 = "12" == c;//return true String d = "123"; String e = new String("12") + new String("3"); e.intern(); boolean flag3 = "123" == e;//return false
注意: 在1.6,str.intern()
- 如果串池中有该字符串对象,就不会放入
- 如果串池中没有该字符串对象,就会先复制一份,再将复制的那份放入(这就会导致放入的对象与原对象不相等),也会将池中的对象返回
以上是关于从字节码分析字符串是否相等的主要内容,如果未能解决你的问题,请参考以下文章