经@断弯刀指正,不同版本jdk编译结果会有所不同。
重新编辑正文,并附上编辑时使用的jdk版本:
java version "1.8.0_191"
Java(TM) SE Runtime Environment (build 1.8.0_191-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.191-b12, mixed mode)
案例1:拼接字符串常量
String s1 = "a" + "b" + "c";
String s2 = new StringBuilder().append("a").append("b").append("c").toString();
反编译class文件
String s1 = "abc";
String s2 = "a" + "b" + "c";
Byte Code
0 ldc <String "abc"> [21]
2 astore_1 [s1]
3 new java.lang.StringBuilder [23]
6 dup
7 invokespecial java.lang.StringBuilder() [25]
10 ldc <String "a"> [26]
12 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [28]
15 ldc <String "b"> [32]
17 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [28]
20 ldc <String "c"> [34]
22 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [28]
25 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [36]
28 astore_2 [s2]
分析:
可以看出,使用 加号 进行字符串常量的拼接在编译时就已经完成,而使用 StringBuilder 进行字符串拼接需要在运行时完成。所以单纯的字符串常量拼接 加号 的效率 应该高于 StringBuilder,下面我们进行测试:
long l1 = System.currentTimeMillis();
for (int i=0; i<1_000_000; i++) {
String s1 = "a" + "b" + "c";
}
System.out.println(System.currentTimeMillis() - l1); //结果: 2
long l2 = System.currentTimeMillis();
for (int i=0; i<1_000_000; i++) {
String s2 = new StringBuilder().append("a").append("b").append("c").toString();
}
System.out.println(System.currentTimeMillis() - l2); //结果 : 19
测试结果与预测一致。
案例2:拼接字符串与引用
String s1 = "a";
String s2 = new StringBuilder().append(s1).append("b").append("c").toString();
String s3 = s1 + "b" + "c";
反编译class文件
String t1 = "a";
String t2 = t1 + "b" + "c";
String t3 = t1 + "bc";
Byte Code
L1
LINENUMBER 6 L1
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
LDC "b"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
LDC "c"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 2
L2
LINENUMBER 7 L2
NEW java/lang/StringBuilder
DUP
INVOKESPECIAL java/lang/StringBuilder.<init> ()V
ALOAD 1
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
LDC "bc"
INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
ASTORE 3
L3
分析:
从反编译的结果可以看出加号拼接的方式在编译过程中完成了"b"、"c"的拼接,而创建 StringBuilder 对象进行拼接需要在运行中进行"b"、"c"的拼接,加号拼接的方式应该更高效,下面是测试结果:
String s1 = "a";
long l1 = System.currentTimeMillis();
for (int i=0; i<1_000_000; i++) {
String s2 = new StringBuilder().append(s1).append("b").append("c").toString();
}
System.out.println(System.currentTimeMillis() - l1); //结果: 66
long l2 = System.currentTimeMillis();
for (int i=0; i<1_000_000; i++) {
String s3 = s1 + "b" + "c";
}
System.out.println(System.currentTimeMillis() - l2);//结果 : 41
测试结果与预期一致。
案例3:循环拼接字符串
通常我们都会对一个对象进行字符串的循环拼接,例如:
String s1 = "";
long l1 = System.currentTimeMillis();
for(int i=0; i<100_000; i++) {
s1 = s1 + "a" + "b" + "c" + "d" + "e" + "f";
}
System.out.println((System.currentTimeMillis() - l1) + " " + s1.length()); //结果 15041 600000
StringBuilder sb = new StringBuilder();
String s2;
long l2 = System.currentTimeMillis();
for(int i=0; i<100_000; i++) {
sb.append("a").append("b").append("c").append("d").append("e").append("f");
}
s2 = sb.toString();
System.out.println((System.currentTimeMillis() - l2) + " " + s2.length()); //结果 6 600000
反编译classes
String s1 = "";
long l1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
s1 = s1 + "a" + "b" + "c" + "d" + "e" + "f";
}
System.out.println(System.currentTimeMillis() - l1 + " " + s1.length());
StringBuilder sb = new StringBuilder();
long l2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
sb.append("a").append("b").append("c").append("d").append("e").append("f");
}
String s2 = sb.toString();
System.out.println(System.currentTimeMillis() - l2 + " " + s2.length());
Byte Code
0 ldc <String ""> [16]
2 astore_1 [s1]
3 invokestatic java.lang.System.currentTimeMillis() : long [18]
6 lstore_2 [l1]
7 iconst_0
8 istore 4 [i]
10 goto 61
13 new java.lang.StringBuilder [24]
16 dup
17 aload_1 [s1]
18 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [26]
21 invokespecial java.lang.StringBuilder(java.lang.String) [32]
24 ldc <String "a"> [35]
26 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
29 ldc <String "b"> [41]
31 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
34 ldc <String "c"> [43]
36 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
39 ldc <String "d"> [45]
41 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
44 ldc <String "e"> [47]
46 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
49 ldc <String "f"> [49]
51 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
54 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
57 astore_1 [s1]
58 iinc 4 1 [i]
61 iload 4 [i]
63 ldc <Integer 100000> [55]
65 if_icmplt 13
68 getstatic java.lang.System.out : java.io.PrintStream [56]
71 new java.lang.StringBuilder [24]
74 dup
75 invokestatic java.lang.System.currentTimeMillis() : long [18]
78 lload_2 [l1]
79 lsub
80 invokestatic java.lang.String.valueOf(long) : java.lang.String [60]
83 invokespecial java.lang.StringBuilder(java.lang.String) [32]
86 ldc <String " "> [63]
88 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
91 aload_1 [s1]
92 invokevirtual java.lang.String.length() : int [65]
95 invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [69]
98 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
101 invokevirtual java.io.PrintStream.println(java.lang.String) : void [72]
104 new java.lang.StringBuilder [24]
107 dup
108 invokespecial java.lang.StringBuilder() [77]
111 astore 4 [sb]
113 invokestatic java.lang.System.currentTimeMillis() : long [18]
116 lstore 6 [l2]
118 iconst_0
119 istore 8 [i]
121 goto 160
124 aload 4 [sb]
126 ldc <String "a"> [35]
128 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
131 ldc <String "b"> [41]
133 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
136 ldc <String "c"> [43]
138 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
141 ldc <String "d"> [45]
143 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
146 ldc <String "e"> [47]
148 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
151 ldc <String "f"> [49]
153 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
156 pop
157 iinc 8 1 [i]
160 iload 8 [i]
162 ldc <Integer 100000> [55]
164 if_icmplt 124
167 aload 4 [sb]
169 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
172 astore 5 [s2]
174 getstatic java.lang.System.out : java.io.PrintStream [56]
177 new java.lang.StringBuilder [24]
180 dup
181 invokestatic java.lang.System.currentTimeMillis() : long [18]
184 lload 6 [l2]
186 lsub
187 invokestatic java.lang.String.valueOf(long) : java.lang.String [60]
190 invokespecial java.lang.StringBuilder(java.lang.String) [32]
193 ldc <String " "> [63]
195 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [37]
198 aload 5 [s2]
200 invokevirtual java.lang.String.length() : int [65]
203 invokevirtual java.lang.StringBuilder.append(int) : java.lang.StringBuilder [69]
206 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [51]
209 invokevirtual java.io.PrintStream.println(java.lang.String) : void [72]
分析:
测试结果 StringBuilder拼接字符串 的效率 远远高于 加号拼接字符串,主要原因在于 加号拼接字符串的方式 每次循环开始时都会创建一个 StringBuilder实例(Byte Code 65行与13行 ps: if_icmplt 条件分支指令 如果一个int类型值小于另外一个int类型值,则跳转),而 StringBuilder拼接字符串 的方式只在循环开始前创建了一个 StringBuilder实例。