通过Java字节码发现有趣的内幕之String篇(一)

AI + 网络安全商业案例白皮书,快来下载!”

很多时候我们在编写 Java 代码时,判断和猜测代码问题时主要是通过运行结果来得到答案,本博文主要是想通过 Java 字节码的方式来进一步求证我们已知的东西。这里没有对 Java 字节码知识进行介绍,如果想了解更多的 Java 字节码或对其感兴趣的朋友可以先阅读字节码基础:JVM 字节码初探

String 字面量可以通过 '==' 判断两个字符串是否相同,是因为大家都知道 '==' 是用来判断两个对象的值引用地址是否一致,两个值一样的字符串字面量定义是否指向同一个值内存地址呢?答案是肯定的。

package com.jaffa.test.string; public class ConstPoolTest { public static void main(String[] args){ String str1 = "strVal_1"; String str2 = "strVal_1"; System.out.printf("str1==str2 is %b",str1==str2);
}
}

代码中声明了 str1 和 str2 的字面量值都为 strVal_1,并且打印出 str1==str2 为 true,说明两个 str1 和 str2 变量同时指向同一个字符串常量值的内存地址,下面通过 Java 字节码来验证这个结果。

在命令行我们通过 javap 工具来查看一个 class 文件的字节码。

javap -v com.jaffa.test.string.ConstPoolTest

   

在 Constant pool 列表中看到 #16 为一个 String 类型并值指向 #17,而 #17 是一个 utf8 字符集编码值为 strVal_1,所以 #16 和 #17 最终表达就是在常量池中有个 String 类型值为 strVal_1 的常量数据。

那接下来需要确认 str1 和 str2 两个变量值是否都是指向 #16 呢?

   

 
 0: ldc           #16     
 2: astore_1              
 3: ldc           #16     
 5: astore_2              

从上面字节码执行来看,str1 和 str2 都是被赋于同一个常量值,由此可以得出两个变更指向同一个内存地址。

通过同样的方式,我们来看一下如果是非字面量的情况会是怎么样的,Java 代码如下:

package com.jaffa.test.string; public class ConstPoolTest { public static void main(String[] args){ String str1 = "strVal_1"; String str2 = new String("strVal_1"); System.out.printf("str1==str2 is %b",str1==str2);
}
}

上面代码输出结果为 false,通过 javap 查看发现 str2 变量的字节码指令发生了变化,如下现两截图:

     

   

 
 0: ldc           #16     
 2: astore_1              
 3: new           #18     
 6: dup                   
 7: ldc           #16     
 9: invokespecial #20     
12: astore_2              

这时 str2 变量是创建一个新的内存地址,而非直接指向 #16 常量内存地址,所以 str1==str2 的结果为 false。同时可以看到通过 new String () 带来看更多的字节码指令操作,运行上花费了更多的系统资源。

下篇:将通过字节码发现 String 操作内幕

首页 - Wiki
Copyright © 2011-2025 iteam. Current version is 2.148.1. UTC+08:00, 2025-11-22 03:10
浙ICP备14020137号-1 $访客地图$