StringTable
例1:
String s1 = "abc";
String s2 = "a"+"b"+"c"; // 在编译时会转换为 String s2="abc";
System.out.println(s1==s2) // true
例2:
String s1 = "abc";
String s2 = "def";
String s3 = "abc"+"def";
String s4 = "abcdef";
String s5 = s1+"def";
String s6 = s1+s2;
// 拼接符号前后出现变量时,相当于new String(),内容为拼接的结果
System.out.println(s4==s5); // false
System.out.println(s4==s5); // false
例3:下列语句会生成几个对象?
String s = new String("ab");
- 上述代码会生成两个对象
- 一个是由new在堆上生成的对象
- 另一个是由字节码 ldc 在String常量池中生成的对象
例4:下列语句会生成几个对象
String s = new String("a") + new String("b");
- 上述代码会生成6个对象
- new String(“a”)和new String(“b”)会分别生成两个对象,合计4个对象
- String拼接生成一个StringBuilder对象
- StringBuilder.toString()会在堆中返回拼接完成后的String对象,此时不会调用 ldc 在常量池中生成该String对象
个人想法:
代码中的明文字符串会自动放入String常量池,因此toString()返回的字符串没有提前放入常量池。而上述两端代码中存在明文的String对象,可能在代码编译时或其他代码运行前的阶段已经放入常量池,之后new关键字又在堆中生成另一个String对象
例5:
public class Main {
public static void main(String[] args){
String s1 = new String("1");
s1.intern();
String s2 = "1";
System.out.println(s1==s2); // jdk6:false jdk7/8:false
String s3 = new String("a")+new String("b");
s3.intern();
String s4 = "ab";
System.out.println(s3==s4); // jdk6:false jdk7/8:true
}
}
- 造成上面情况的原因在于jdk6和jdk7/8中intern()的执行机制不同
- jdk6:在String常量池中生成调用该方法的String对象副本
- jdk7/8:在String常量池中添加调用该方法的String对象的引用。若常量池中本来就存在该String对象,则返回原本常量池里的String对象的引用。后面使用该对象的明文字面量时,仍然是根据地址取到最初的实例对象
参考b站尚硅谷宋红康jvm课程