为了您更好的理解本篇文章,请先查阅
https://blog.csdn.net/define_us/article/details/78252783 中的JVM分代管理策略一节。
JDK的String常量池存在于Perm区,和JAVA Heap区是完全不同的两个区域。通过字符串常量生成的String对象存在在永久代上,而通过new产生的对象,分配在JAVA HEAP上。intern会查看是否有String常量池的值等于当前字符串,如果没有,把在常量池新建该值的字符串对象。然后把常量池中的字符串对象返回。
public static void main(String[] args) {
//指向常量池中的同一个字符串对象
String s5 = "111";
String s6 = "111";
System.out.println(s5 == s6);//true
//s8是intern后返回的常量池中的字符串对象
String s7 = "111";
String s8 = new String("111").intern();
System.out.println(s7 == s8);//true
String s = new String("1");
s.intern();
String s2 = "1";
System.out.println(s == s2);//false
String s3 = new String("1") + new String("1");
s3.intern();
String s4 = "11";
System.out.println(s3 == s4);//false
}
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3) + "";
str = null;
运行结束后str内部的char数组不会被回收,因为substring是通过调整边界实现的。在str被置为null后,sub内部仍然引用者char数组。
public String substring(int var1, int var2) {
return var1 == 0 && var2 == this.count ?
this : new String(this.offset + var1, var2 - var1, this.value);//这里复用了当前字符串的value数组。
}
String(int var1, int var2, char[] var3) {
this.value = var3;
this.offset = var1;
this.count = var2;
}
采用如下方式就可以解决该问题。
String str = "abcdefghijklmnopqrst";
String sub = str.substring(1, 3) + "";
str = null;
public static void main(String[] args) {
String s3 = new String("1") + new String("1");
String s5 = s3.intern();
String s4 = "11";
System.out.println(s3 == s4);
System.out.println(s3 == s5);
}
}
上述的代码,在JDK6中都是false,但是在JDK7中则都是true。
jdk1.6中,s3.intern()运行时,首先去常量池查找,发现没有该常量,则在常量池中开辟空间存储"11",返回其地址(S5),第三行中,s4通过查找常量池也指向了常量池存储的对象。所以S3是堆中的对象,S4,S5是常量池中对象。
jdk1.7中,由于常量池放到了堆空间中,所以在s3.intern()运行时,发现常量池没有常量,则添加常量并使其指向堆中的地址,返回堆中的地址(注意这里也没有使用该返回值) 。这时s4通过查找常量池中的常量,找到了堆中的地址并指向它,所以S3 = S4;
如果改变下顺序,则在JDK中的结果就都是false。这是因为直接使用字符串常生成的String也会被分配在常量迟,
public static void main(String[] args) {
String s4 = "11";
String s3 = new String("1") + new String("1");
String s5 = s3.intern();
System.out.println(s3 == s4);//false
System.out.println(s3 == s5);//false
}
在JDK8中运行上面的两个例子,上述代码和JDK7中的结果相同。唯一有改变的split的实现
public static void main(String[] args) {
String str = "ABC";
// 测试String#split()方法
String[] elems = str.split("");
System.out.println(Arrays.toString(elems));
// 结果:
// Java 7 -> [, A, B, C]
// Java 8 -> [A, B, C]
}