1. String拼接
1.1. 变量拼接
阅读下面的代码,推测结果。
public class TestString {
public static void main(String[] args) {
String s1="a";
String s2="b";
String s3="ab";
String s4=s1+s2;
System.out.println(s3==s4);
}
}
结果:false
对于s1、s2、s3所引用的字符串”a”、”b”、”ab”,都是存放在StringTable中的。
要想知道s3和s4的“==”的结果,需要知道String类型做“+”运算的机制。
通过javap -v TestString
对class文件进行反编译,查看“s4=s1+s2”这部分的字节码,如下所示:
9: new #5 // class java/lang/StringBuilder
12: dup
13: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
16: aload_1
17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: aload_2
21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
从上面的字节码已经可以看出String类型做“+”运算的内部原理是创建了一个StringBuilder实例,然后进行append()
调用,最后调用toString()
方法返回一个新的String对象的引用。
对于s3指向的是StringTable中的”ab”字符串,s4指向堆中的一个String对象,所以s3和s4不相等。
1.2. 常量拼接
public class TestString2 {
public static void main(String[] args) {
String s1="a";
String s2="b";
String s3="ab";
String s4="a"+"b";
System.out.println(s3==s4);
}
}
对于上面的代码,打印结果是true,即s3和s4相等,因为s4等于常量相加,结果也是常量,会存在StringTable中,s3和s4同时指向StringTable中的”ab”字符串。
2. intern()
对于new创建的String对象,调用intern()
方法:检查字符串常量池(StringTable)中是否存在自己的字符串值,如果存在,直接返回StringTable中的引用,否则将自己的引用存入字符串常量池并返回。
2.1. Example
观察下面两段测试代码,预测结果。
public class TestStringIntern {
public static void main(String[] args) {
String s1=new String("a")+new String("b");
s1.intern();
String s2="ab";
System.out.println(s2==s1);
}
}
public class TestStringIntern {
public static void main(String[] args) {
String s1=new String("a")+new String("b");
String s2="ab";
s1.intern();
System.out.println(s2==s1);
}
}
上面两段代码第4、5行代码顺序相反。上面的代码输出结果为true,下面的代码输出结果为false。
2.2. 分析
从main方法里面的代码开始分析:
String s1=new String("a")+new String("b");
在堆中创建了两个String对象,值分为别”a”、”b”。同时由于构造方法参数是字符串常量,会在堆中创建两个String对象,String Table维护这两个对象的引用(这时候这两个对象就可以视为常量了)。
- 对于上面的代码,
s1.intern()
去检查常量池中有没有值为”ab”的对象,由于没有,将s1指向的对象地址添加到常量池,维护这个String对象,随后String s2="ab"
会在常量池查找有没有值为”ab”的对象,由于存在这个对象即s1指向的对象,直接返回这个对象的地址,所以s1==s2为true。 - 对于下面的代码。
String s2="ab"
会在常量池查找有没有值为”ab”的对象,由于没有,会在堆中创建一个值为”ab”的对象,并把地址添加String table以维护这个对象。随后s1.intern()
检查常量池,由于值为”ab”的对象已经存在,不做任何操作,因此s1和s2指向的是不同的对象,s1==s2为false。