String在JDK6 JDK7 JDK8之中的变化

为了您更好的理解本篇文章,请先查阅
https://blog.csdn.net/define_us/article/details/78252783 中的JVM分代管理策略一节。

JDK6

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
        
    }
  • JDK6有一个硬伤,就是subString的内存泄漏问题
    如下代码:
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;

JDK7


    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

在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]
    }

你可能感兴趣的:(我们爱历史)