Java中String.intern()方法浅记
String在Java中是一个比较特殊的存在。String既可以使用字面量进行赋值,也可以使用new进行赋值。
- 使用字面量进行赋值,会直接放进字符串常量池
- 如果使用new进行创建对象,会在堆中生辰给一个对象,也会在常量池中存放一份,两者并不相同。
注意:
字符串常量池从jdk7开始,从方法区移到了堆中。静态变量也从方法区中移到了堆中。
概念
使用intern()方法,会先判断字符串常量池中是否已经包含当前的字符串,如果没有的话,则会在字符串常量池中存放一份。在具体的实现中稍有不同。
- 在jdk6之前(包含jdk6),字符串常量池在方法区中,与堆不在同一个区域,所以字符串常量池中是拷贝了一份字符串的对象,Java堆中的String对象和字符串常量池的String对象不是同一个。
- 在jdk7之后(包含jdk7),字符串常量池转移到了堆中。当堆中有一个String对象的时候,在调用intern方法时,如果字符串常量池没有这个字符串,则会存放堆中String对象的地址引用,不会像jdk7之前的拷贝一份到字符串常量池中。此时,字符串常量池翠芳的和堆中String对象代表的是一个。
如果有描述不清处的欢迎大家查看《深入了解java虚拟机的》第三版的63页。字符串常量池的迁移查看46页。
代码验证
**环境:**jdk1.8
主要验证的是jdk8,也就是字符串常量池迁移到堆的情况,jdk6之前的情况,感兴趣的可以自己尝试。
测试方法:
第一种情况:
public class InternTest {
public static void main(String[] args) {
String str1 = new String("jay,cay");
str1.intern();
String str2 = "jay,cay";
System.out.println(str1 == str2);
}
}
测试结果:
false
分析:
在new的时候会在堆中有一个,在字符串常量池中有一个。所以intern方法相当于没有起作用,因为因为字符串常量池中已经有一个相同值的对象
第二种情况:
public class InternTest {
public static void main(String[] args) {
String str1 = new String("jay,cay");
str1.intern();
String str2 = "jay,cay";
System.out.println(str1 == str2);
String str3 = new String("aa") + new String("bb");
str3.intern();
String str4 = "aabb";
System.out.println(str3 == str4);
}
}
测试结果:
false
true
分析:
由于两个new字符串的拼接,所以在字符串长量池中不会有aabb,下面的intern方法会将堆中的aabb的引用存放在字符串常量池中。这样str3和str4就相同了。
反思:
实际中使用的StringBuilder产生的字符串不在字符串常量池中的多,下面这段代码可以在《深入了解Java虚拟》63页找到。
String str5 = new StringBuilder("计算机").append("软件").toString();
str5.intern();
String str6 = "计算机软件";
System.out.println(str5 == str6);
特例:
在《深入了解Java虚拟》63页提到了一个特例,就是java这个字符串,使用下面的方式得到的是false,原因是在项目启动的时候默认会加载一个java字符串在字符串常量池。
String str7 = new StringBuilder("ja").append("va").toString();
str5.intern();
String str8 = "java";
System.out.println(str7 == str8);
疑惑:待解决
之前遇到过使用junit4的时候,使用下面的代码得到的都是false,而且在main方法中添加org.slf4j.Logger(在intern方法之前),进行打印日志也会得到的都是false,使用的springboot时2.3.1。本次在junit5中测试,使用的spring boot2.6.2,得到的结果,和上面代码得到的结果一致。
测试方法代码如下:
@ExtendWith(SpringExtension.class)
@SpringBootTest
public class InternTest {
@Test
public void testIntern(){
String str1 = new String("jay,cay");
str1.intern();
String str2 = "jay,cay";
System.out.println(str1 == str2);
String str3 = new String("aa") + new String("bb");
str3.intern();
String str4 = "aabb";
System.out.println(str3 == str4);
}
}
|