测试环境为 JDK1.8
测试 1
String s1 = "abc";
String s2 = "abc";
System.out.println(s1 == s2);
显示结果为 true。
创建 String 字面量时,虚拟机在编译期间把该字面量放到 字符串常量池 中。创建 s2 所代表的 “abc” 时,首先检查常量池中是否已经存在相同的字面量(此处已经存在 s1 代表的 “abc” 了),如果存在直接返回该字面量的引用,否则创建新的字符串。所以 s2 和 s1 是同样的引用,完全相等。
如下图所示:
测试 2
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);
System.out.println(s1.equals(s2));
显示结果为 false, true。
“new String(“abc”)” 首先在字符串常量池中创建字面量 “abc”(此处由于 s1 已经创建过了,所以不会再重复创建),然后在堆中创建 String 对象。所以这一句话将会让字符串常量池和 堆内存 中都有 “abc”。
s1 持有字符串常量池中的 “abc” 的引用,s2 持有堆中的引用(堆中不保留“abc”,只保留引用),显然不在同一内存中,所以使用 “==” 比较时结果为 false。使用 “equals” 方法只比较它们的值,所以返回 true。
如下图所示:
测试 3
String s1 = "ab";
String s2 = "a" + "b";
String s3 = "a" + new String("b");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
显示结果为 true, false。
s2 由两个字符串常量连接而成,s2 也是字符串常量,在编译器就能确定,直接存在字符串常量池中。
s3 由堆中的字符串对象 “b” 生成,s3 也是对象,直接存在堆内存中。完成后回收堆中的 “b”。
如下图所示:
测试 4
String s1 = new String("a");
String intern1 = s1.intern();
System.out.println(s1 == intern1);
显示结果为 false。
当调用intern方法时,如果字符串常量池中已经包含与 equals 方法确定的相当于此String 对象的字符串,则返回来自常量池的字符串。 否则,将此 String 对象添加到池中,并返回对常量池中此 String 对象的引用。
如下图所示:
测试 5
String s1 = new String("a") + new String("b");
String intern1 = s1.intern();
System.out.println(s1 == intern1);
String s2 = "ab";
System.out.println(intern1 == s2);
首先在常量池中创建 “a” 和 “b”,然后在堆中创建 “ab”,并将 s1 指向堆中该对象。由于常量池中没有 “ab”,调用 intern 函数返回堆里的 “ab” 对象的地址,并将其添加到常量池中。最后 s2 指向常量池中该对象。
如下图所示:
对于上面程序中的加号:
若 + 号前后为常量,编译期会处理。
若 + 号前后存在着变量,首先以最左边的字符串为参数创建 StringBuilder 对象,然后依次对右边进行 append 操作,最后将 StringBuilder 对象通过 toString() 方法转换成 String 对象。
参考