拼接时无变量

String s = "a" + "b" + "c";

拼接的时候没有变量,都是字符串。触发字符串的优化机制。
在编译的时候就已经是最终的结果了。

即当Java文件编译成字节码文件( .class)时,会检查是否有变量参与,如果没有,则直接生成最终结果。上述代码经编译后的结果为:

String s = "abc";

拼接时有变量

在JDK8以前
String s1 = "a";
String s2 = s1 + "b";
String s3 = s2 + "c";

当上述代码运行时:

  1. 首先s1="a",会再StringTable串池里生成一个字符串"a",再将串池中字符串"a"的地址给到s1;
  2. s2=s1+"b",串池中没有字符串"b",故需要在串池中生成一个字符串"b",该表达式中有变量,所以Java会自动在内存中创建一个StringBuilder对象,然后通过append()方法把s1和"b"都放到对象中,然后再将StringBuilder对象通过toString()方法转换成String对戏。
  3. s3=s2+"c",与上相同,会再创建一个StringBuilder对象,使用append()方法做拼接。

整个过程相当于:

new StringBuilder().append(s1).append("b").toString();

通过分析StringBuiler的源码,可知字符串和变量一次拼接,内存中至少会有两个对象,一个是StringBuilder对象,一个是String对象。

image-20220903160211455
image-20220903160211455

由此看出这样拼接,速度很慢,浪费内存。

JDK8及以后
String s1 = "a";
String s2 = "b";
String s3 = "c";
String s4 = s1 + s2 + s3;

运行上述程序后,Java会根据s1、s2、s3预估s4的长度,然后创建一个数组,若为上述所示代码,会创建一个长度为3的数组,将"a","b","c"放入数组中,然后将数组变为一个字符串。

但当有多个变量参与拼接时,预估最终字符串长度会耗费很多时间,在内存中也创建了很多对象,也会浪费空间。所以若有很多字符串变量拼接,不要直接+,而是使用StringBuilder或者StringJoiner。


易混淆点

String s1 = "abc";
String s2 = "ab";
String s3 = s2 + "c";
System.out.println(s1==s3);

输出结果应为:false

不管在JDK8之前还是在JDK8,字符串拼接时最终都会new一个String对象出来。

JDK8之前:调用StringBuilder对象-->append()方法拼接-->toString方法(toString方法底层实际是new了一个String对象)

JDK8:预估最终字符串长度-->将要拼接内容放入数组-->产生新的String对象

String s1 = "abc";
String s2 = "a" + "b" + "c";
System.out.println(s1==s2);

输出结果应为:true

s2在编译时就已经被拼接完成了,所以s1,s2在串池中的地址值相同,输出true。