字符串拼接:辨析字面量拼接和对象拼接对结果类型的影响

129 阅读3分钟
public class StaticTest {
    public static void main(String[] args){
       String s1 = "java";
       String s2 = "java";
       String s3="ja"+"va";
       System.out.println(s2==s3);//是true
​
       String s="abc";
       String ss="c";
       String sss="ab";
       String ssss=sss+ss;
       System.out.println(ssss==s);//是false
    }
}
​

省流版:

1.java的拼接采用的是"ja"和"va"。使用字面量拼接后,编译器会优化这个行为,拿最终拼成的结果在字符串常量池判定有无也已经存在的。如果已经存在就复用已存在的字符串因此s2和s3的地址是一致的,因为其复用了同一个字符串

2.ssss的拼接是使用引用进行拼接,也就是sss和ss,使用引用拼接相较于使用字面量,会采取StringBuilder来工作,而StringBuilder产生的对象是在堆区,而非是字符串常量池

3.总的来说:"java"的拼接结果存在于字符串常量池,而ssss的拼接结果在堆区。这是因为拼接原料的类型不同导致的,使用字面量拼接,倘若拼接结果在字符串常量池中有可复用的,则不需要创建新对象。但倘若使用引用进行拼接,那无论字符串常量池有无可复用元素,都在堆区创建一个新的String.

代码解析

public class StaticTest {
    public static void main(String[] args) {
        String s1 = "java";
        String s2 = "java";
        String s3 = "ja" + "va";
        System.out.println(s2 == s3);  // 第一个输出
​
        String s = "abc";
        String ss = "c";
        String sss = "ab";
        String ssss = sss + ss;
        System.out.println(ssss == s);  // 第二个输出
    }
}

第一部分分析:s2 == s3

String s1 = "java";
String s2 = "java";
String s3 = "ja" + "va";
System.out.println(s2 == s3);  // 第一个输出
  1. 字符串常量池:在 Java 中,所有字符串常量(通过字面值 "xxx" 创建的字符串)都会自动存储在 字符串常量池 中。

    • s1s2 都被赋值为字面量 "java",因此它们指向常量池中的同一个对象。
    • 在这行代码中,s2s3 都是字符串 "java",但它们的创建方式略有不同。
  2. 字符串拼接

    • s3 是通过 "ja" + "va" 拼接得到的字符串。由于 "ja""va" 都是字面量,因此 Java 会在编译时将它们拼接成 "java",并直接在编译时生成一个常量值 "java"
    • 所以,s3 也会指向常量池中的 "java"
  3. == 比较

    • s2s3 都指向常量池中的 "java",因此 s2 == s3true

第二部分分析:ssss == s

String s = "abc";
String ss = "c";
String sss = "ab";
String ssss = sss + ss;
System.out.println(ssss == s);  // 第二个输出
  1. 字符串拼接

    • 变量 ssss 是通过 sss + ss 拼接得到的字符串。这里 sssss 都是字符串变量,而不是常量(即它们不是字面量)。在这种情况下,字符串拼接通常在 运行时 进行。
    • ssss = sss + ss; 在编译时无法确定拼接的结果,因为 sssss 变量的值是在运行时确定的。因此,Java 会在运行时创建一个新的 StringBuilder 对象来执行拼接,并生成一个新的 String 对象。
  2. 常量池与运行时对象

    • s = "abc"; 由于 "abc" 是字面量,它会直接指向常量池中的 "abc" 字符串。
    • ssss 则是通过运行时拼接的字符串,它不是常量池中的对象。因此,ssss 是一个新的 String 对象,不是常量池中的 "abc"
  3. == 比较

    • ssss == s 比较的是两个不同的对象:一个是常量池中的 "abc",另一个是通过拼接创建的新对象。由于它们是不同的对象(即指向不同的内存位置),因此 ssss == sfalse

总结

  1. s2 == s3 输出 true,因为 s2s3 都是编译时常量,指向字符串常量池中的 "java",它们引用的是同一个对象。
  2. ssss == s 输出 false,因为 ssss 是在运行时通过拼接生成的新的字符串对象,它和常量池中的 "abc" 不指向同一内存位置,ssss 是一个新的 String 对象。

输出结果:

true
false