java字符串连接StringBuilder,StringBuffer和+拼接区别是什么
环境
JDK17
StringBuffer和StringBuilder
StringBuffer和StringBuilder都继承了AbstractStringBuilder抽象类,底层都是可变的字符数组,所以在进行频繁的字符串操作时,建议使用StringBuffer和StringBuilder来进行操作。 StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
代码
public class StringConcatenationInLoop {
public static void main(String[] args) {
String result = "";
for (int i = 0; i < 10; i++) {
result = new StringBuilder().append(result).append(i).toString();
}
System.out.println(result);
}
}
编译后
// Source code is decompiled from a .class file using FernFlower decompiler.
package cn.goylsf.str;
class StringConcatenationInLoop {
StringConcatenationInLoop() {
}
public static void main(String[] args) {
String result = "";
for(int i = 0; i < 10; ++i) {
result = result + i;
}
System.out.println(result);
}
}
javap -c -v .\StringConcatenationInLoop.class
Classfile /C:/Java/workspace/myLabs/JavaBaseLab/target/classes/cn/goylsf/str/StringConcatenationInLoop.class
Last modified 2025年4月5日; size 953 bytes
SHA-256 checksum 2fd3c18a078351b593adc3f73d142ea4d2014046ce3733a939bfd10547b48250
Compiled from "StringConcatenationInLoop.java"
class cn.goylsf.str.StringConcatenationInLoop
minor version: 0
major version: 52
flags: (0x0020) ACC_SUPER
this_class: #1 // cn/goylsf/str/StringConcatenationInLoop
super_class: #3 // java/lang/Object
interfaces: 0, fields: 0, methods: 2, attributes: 1
Constant pool:
#1 = Class #2 // cn/goylsf/str/StringConcatenationInLoop
#2 = Utf8 cn/goylsf/str/StringConcatenationInLoop
#3 = Class #4 // java/lang/Object
#4 = Utf8 java/lang/Object
#5 = Utf8 <init>
#6 = Utf8 ()V
#7 = Utf8 Code
#8 = Methodref #3.#9 // java/lang/Object."<init>":()V
#9 = NameAndType #5:#6 // "<init>":()V
#10 = Utf8 LineNumberTable
#11 = Utf8 LocalVariableTable
#12 = Utf8 this
#13 = Utf8 Lcn/goylsf/str/StringConcatenationInLoop;
#14 = Utf8 main
#15 = Utf8 ([Ljava/lang/String;)V
#16 = String #17 // 1
#17 = Utf8 1
#18 = Class #19 // java/lang/StringBuilder
#19 = Utf8 java/lang/StringBuilder
#20 = Methodref #21.#23 // java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#21 = Class #22 // java/lang/String
#22 = Utf8 java/lang/String
#23 = NameAndType #24:#25 // valueOf:(Ljava/lang/Object;)Ljava/lang/String;
#24 = Utf8 valueOf
#25 = Utf8 (Ljava/lang/Object;)Ljava/lang/String;
#26 = Methodref #18.#27 // java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
#27 = NameAndType #5:#28 // "<init>":(Ljava/lang/String;)V
#28 = Utf8 (Ljava/lang/String;)V
#29 = Methodref #18.#30 // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
#30 = NameAndType #31:#32 // append:(I)Ljava/lang/StringBuilder;
#31 = Utf8 append
#32 = Utf8 (I)Ljava/lang/StringBuilder;
#33 = Methodref #18.#34 // java/lang/StringBuilder.toString:()Ljava/lang/String;
#34 = NameAndType #35:#36 // toString:()Ljava/lang/String;
#35 = Utf8 toString
#36 = Utf8 ()Ljava/lang/String;
#37 = Fieldref #38.#40 // java/lang/System.out:Ljava/io/PrintStream;
#38 = Class #39 // java/lang/System
#39 = Utf8 java/lang/System
#40 = NameAndType #41:#42 // out:Ljava/io/PrintStream;
#41 = Utf8 out
#42 = Utf8 Ljava/io/PrintStream;
#43 = Methodref #44.#46 // java/io/PrintStream.println:(Ljava/lang/String;)V
#44 = Class #45 // java/io/PrintStream
#45 = Utf8 java/io/PrintStream
#46 = NameAndType #47:#28 // println:(Ljava/lang/String;)V
#47 = Utf8 println
#48 = Utf8 args
#49 = Utf8 [Ljava/lang/String;
#50 = Utf8 result
#51 = Utf8 Ljava/lang/String;
#52 = Utf8 i
#53 = Utf8 I
#54 = Utf8 StackMapTable
#55 = Utf8 SourceFile
#56 = Utf8 StringConcatenationInLoop.java
{
cn.goylsf.str.StringConcatenationInLoop();
descriptor: ()V
flags: (0x0000)
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 3: 0
LocalVariableTable:
Start Length Slot Name Signature
0 5 0 this Lcn/goylsf/str/StringConcatenationInLoop;
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: (0x0009) ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=3, args_size=1
0: ldc #16 // String 1
2: astore_1
3: iconst_0
4: istore_2
5: goto 30
8: new #18 // class java/lang/StringBuilder
11: dup
12: aload_1
13: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String;
16: invokespecial #26 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
19: iload_2
20: invokevirtual #29 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
23: invokevirtual #33 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26: astore_1
27: iinc 2, 1
30: iload_2
31: bipush 10
33: if_icmplt 8
36: getstatic #37 // Field java/lang/System.out:Ljava/io/PrintStream;
39: aload_1
40: invokevirtual #43 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
43: return
LineNumberTable:
line 5: 0
line 6: 3
line 7: 8
line 6: 27
line 9: 36
line 10: 43
LocalVariableTable:
Start Length Slot Name Signature
0 44 0 args [Ljava/lang/String;
3 41 1 result Ljava/lang/String;
5 31 2 i I
StackMapTable: number_of_entries = 2
frame_type = 253 /* append */
offset_delta = 8
locals = [ class java/lang/String, int ]
frame_type = 21 /* same */
}
SourceFile: "StringConcatenationInLoop.java"
字符串拼接的优化机制变化底层实现原理的演进
- JDK 8 及之前的策略
- 编译器静态优化:将字符串的 + 操作直接转换为显式的 StringBuilder.append() 调用
- 生成固定模式的字节码:通过查看反编译代码可见连续的 append() 和 toString() 调用。
- JDK 9+ 的 StringConcatFactory 机制
- 动态调用站点绑定:通过 invokedynamic 指令在首次调用时绑定最优策略
- 六种优化策略可选: o BC_SB(等价于旧版 StringBuilder) o BC_SB_SIZED(预计算长度的 StringBuilder) o BC_SB_SIZED_EXACT(精确长度预计算) o MH_SB(方法句柄组合) o MH_INLINE_SIZED_EXACT(内联复制) o 默认策略(自动选择最合适方案)。