首先intern()方法的定义:当调用这个方法的时候,如果字符串常量池中有这个对象,就把常量池中的这个对象返回,没有就把当前对象加入到常量池并且返回当前对象的引用;
jdk1.6之前:将对象存入常量池;
jdk1.7: 将堆中引用存入常量池(字符串常量池位于堆中)

public static void main(String[] args) {
    String s = new String("1");
    s.intern();
    String s2 = "1";
    System.out.println(s == s2);

    String s3 = new String("1") + new String("1");
    s3.intern();
    String s4 = "11";
    System.out.println(s3 == s4);
}

打印结果是

jdk6 下false false
jdk7 下false true

为什么jdk7下s3 ==s4呢不是两个完全不同的对象嘛

String s3 = new String(“1”) + new String(“1”);,这句会生成字符串常量池中的“1” 和 JAVA Heap 中的 s3引用指向的对象。中间还有2个匿名的new String(“1”)我们不去讨论它们。此时s3引用对象内容是”11”,但此时常量池中是没有 “11”对象的。
接下来s3.intern();这一句代码,不需要再存储一份对象了,可以直接存储堆中的引用。这份引用指向 s3 引用的对象。 也就是说引用地址是相同的。
最后String s4 = “11”; 这句代码中”11”是显示声明的,因此会直接去常量池中创建,创建的时候发现已经有这个对象了,此时也就是指向 s3 引用对象的一个引用。所以 s4 引用就指向和 s3 一样了。因此最后的比较 s3 == s4 是 true。

再看 s 和 s2 对象。 String s = new String(“1”); 第一句代码,生成了2个对象。常量池中的“1” 和 JAVA Heap 中的字符串对象。s.intern(); 这一句是 s 对象去常量池中寻找后发现 “1” 已经在常量池里了。

jdk1.7中的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用.

另外两个字符串new String(“1”) + new String(“1”) 相加其实就等于调用StringBuilder.append进行拼接然后调用其

@Override
    public String toString() {
        // Create a copy, don't share the array
        return new String(value, 0, count);
    }

注意这个里面并没有new String(“11”)的操作,直接调用的是String的另外一个构造方法所以不会在字符串常量池中创建“11”这个字符串;

拓展
关于java字符串相加

String str1 ="a";
String str2 ="b";
String str3 = str1 + str2;
String str4 = "a" + "b";
//这两者实现过程不一样

很容易发现是str3通过StringBuilder类进行拼接得来的

Java intern函数 java中的intern_字符串


str4是直接得来的,因此在字符串相加时候如果没有用到类似于str1之类的变量的话,就是直接拼接而来,否则就是另外一种情况了。

Java intern函数 java中的intern_字符串_02