在阅览 周志明《深入了解Java虚拟机时》,遇到了一个经典的事例,了解时花费了较多时刻,这儿分享一下自己的考虑。
事例
public void test01() {
String str1=new StringBuilder("计算机").append("软件").toString();
System.out.println(str1.intern()==str1);//true
String str2=new StringBuilder("ja").append("va").toString();
System.out.println(str2.intern()==str2);//false
}
预备常识
在JDK 7以及以上的版别中,因为字符串常量池和String目标都处在 堆 上,因而字符串常量池贮存的是对堆中字符串目标的引证。
intern()办法的作用:假如字符串是第一次呈现,该办法会将字符串参加字符串常量池中;假如不是第一次呈现,那么会将字符串在字符串常量池中的引证回来。
原因解说
那么为什么会呈现上面的现象呢?
需求留意的是,字符串在Java中也是一类特别的目标,只不过因为它常运用作了特别的优化(如字符串常量池)。
在第一个事例中,因为”计算机软件”是第一次呈现,所以intern()办法回来的便是str1的地址。
在第二个事例中,因为”java”先前呈现过,因而intern()办法回来的是先前呈现的”java”字符串地址(在sun.misc.Version中被加载)
图示如下:
部分考虑
- 在阅览书本时,有过疑问为什么要运用StringBuilder而不直接运用String?
- 为什么特意append?
public void test02() {
String str1=new StringBuilder("计算机软件").toString();
System.out.println(str1.intern()==str1);//false
String str2=new StringBuilder("java").toString();
System.out.println(str2.intern()==str2);//false
}
public void test03() {
String str1="计算机"+"软件";
System.out.println(str1.intern()==str1);//true
String str2="ja"+"va";
System.out.println(str2.intern()==str2);//true
}
能够看出,这儿是运行结果和原事例中是不同的。
要了解这个问题,需求先知道
- 在任何地方呈现的被””包裹的字符串都会被参加常量池
- JVM会将形如”ja”+”va”的字符串自动优化为”java”再参加字符串常量池
- StringBuilder在运用toString()办法时会产生新的字符串目标,不会从字符串常量池中获取,而且不会将生成的字符串参加字符串常量池 参考博客
1、运用StringBuider以及append()的原因:
而原事例test01()中,经过StringBuilder的append()办法奇妙地避开了JVM会将形如”ja”+”va”的字符串自动优化为”java”再参加字符串常量池的特性,因而能够在之后调用intern()办法将字符串参加常量池。以Str1为例,会将”计算机”和”软件”别离参加字符串常量池,而不会将”计算机软件”参加常量池。
2、test02()中都为false的原因:
因为特性在任何地方呈现的字符串都会被参加常量池,在test02()中先创建了String目标”计算机软件”,直接将”计算机软件”参加到字符串常量池中了,再将”计算机软件”转为StringBuild,最终再次经过toString()转为str1,这儿的str1为新的字符串目标。而str1.intern()调用是回来的是”计算机软件”的地址,而不是str1的地址
3、test03()中都为true的原因:
直接运用String时,java虚拟时机自动优化,将”ja”和”va”自动合并成”java”,而且因为在字符串常量池中存在,会直接回来字符串常量池中的引证。值得指出的是,这儿的str1.intern()==str1的原因和原事例中是不同的,这儿的str1在创建时就被参加到字符串常量池,因而str1.intern()在这儿并不会执即将str1参加常量池的行为,而是直接回来字符串常量池中队str1的引证。
因而StringBuilder和append()办法缺一不可
因为笔者也刚接触JVM,写的不好望指正