solitaryclown

String类型操作详解

2022-01-20
solitaryclown

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。

string table存的都是引用,对象体都存在堆中。 附上一张图片便于理解: 7gDxbT.jpg


上一篇 JVM

Comments

Content