理清java字符串常量池
Java7后,字符串常量池从方法区移入堆中,同时String的intern()方法也从复制更改为了引用。
本文实例基于JRE1.8
1. 直接在字符串常量池生成,相等
@Test
public void test1() {
// 使用""构造的字符串,直接在字符串常量池生成
String str1 = "abcd";
String str2 = "ab" + "cd";
final String ab = "ab";
final String cd = "cd";
String str3 = ab + cd;
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str3 == str3);
// 运行结果
// true
// true
// true
}
使用idea查看其源代码,可知道编译器会对“”字符串进行优化,如下
@Test public void test1() { String str1 = "abcd"; String str2 = "abcd"; String ab = "ab"; String cd = "cd"; String str3 = "abcd"; System.out.println(str1 == str2); System.out.println(str1 == str3); System.out.println(str3 == str3); }
2. 特殊的String类
根据源码注释,我们可知:
- 构造方法,构造方法先去字符串常量池判断是否存在,不存在创建,然后会在堆中创建一个拷贝(copy),也就是说引用的是堆中的拷贝;
- intern()方法,先去字符串常量池判断是否存在,不存在在字符串常量池保存其堆中的引用,然后返回常量池中的对象;
@Test public void test2() {
// new String()引用的是堆中的拷贝,因此与字符串常量池不相等,inert()也不会指向对于的堆中 String str1 = new String("abcd"); String str2 = str1.intern(); String str3 = "abcd"; System.out.println(str1 == str2); System.out.println(str1 == str3); System.out.println(str2 == str3); // 运行结果 // false // false // true }
@Test
public void test3() {
// 使用StringBuilder、+=、new String() + 等直接在堆中生成,其对于的字符串若第一次调用intern()方法,会直接在字符串常量池保存其引用
// StringBuilder sb = new StringBuilder();
// sb.append("ab").append("cd");
// String str1 = sb.toString();
// String str1 = "ab";
// str1 += "cd";
String str1 = new String("ab") + new String("cd");
String str2 = str1.intern();
String str3 = "abcd";
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str2 == str3);
// 运行结果
// true
// true
// true
}
@Test
public void test4() {
// 更换位置,使得先在字符串常量池中生成"abcd",那么intern()只会返回值,而不是指向堆中再返回
String str1 = new String("ab") + new String("cd");
String str3 = "abcd";
String str2 = str1.intern();
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str2 == str3);
// 运行结果
// false
// false
// true
}
3. 总结时间
- 从字符串常量池中获取的,都相等,如String str = "abcd"、str.intern()等;
- 从堆中获取的,都不相等,如new String()、StringBuild.toString()、+=等;
- 特殊情况,从堆中获取的,若其对于的字符串第一次调用intern()方法,那么后续从字符串常量池获取的都与他相等(new String("abcd")除外,他先在字符串常量池创建,然后在堆中创建副本);