public static void main(String[] args) {
String testStr = "test";
String rst = testStr + 1 + "a" + "pig" + 2;
System.out.println(rst);
}
复制代码
public static void main(String[] args) {
String testStr = "";
for (int i = 0; i < 10; i++) {
testStr += i + "test";
}
System.out.println(testStr);
}
复制代码
例1
中代码的class文件获得以下字节码表示:public static Method main:"([Ljava/lang/String;)V"
stack 3 locals 3
{
ldc String "test";
astore_1;
new class java/lang/StringBuilder;
dup;
aload_1;
invokestatic Method java/lang/String.valueOf:"(Ljava/lang/Object;)Ljava/lang/String;";
invokespecial Method java/lang/StringBuilder."<init>":"(Ljava/lang/String;)V";
iconst_1;
invokevirtual Method java/lang/StringBuilder.append:"(I)Ljava/lang/StringBuilder;";
ldc String "a";
invokevirtual Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;";
ldc String "pig";
invokevirtual Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;";
iconst_2;
invokevirtual Method java/lang/StringBuilder.append:"(I)Ljava/lang/StringBuilder;";
invokevirtual Method java/lang/StringBuilder.toString:"()Ljava/lang/String;";
astore_2;
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;";
aload_2;
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V";
return;
}
复制代码
可见javac自动将这种连续的字符串拼接编译成了StringBuilder.append,所以写代码时不用本身刻意写成StringBuilder.append形式;例2
中代码的class文件获得以下字节码表示:public static Method main:"([Ljava/lang/String;)V"
stack 3 locals 3
{
ldc String ""; // 栈上来个空字符串
astore_1; // 栈顶元素弹出,放到局部变量1
iconst_0; // 栈上来个0
istore_2; // 栈顶元素弹出,放到局部变量2
goto L35; // 跳转到35行,此时栈空
L8: stack_frame_type append;
locals_map class java/lang/String, int;
new class java/lang/StringBuilder; // 栈上来个StringBuilder对象
dup; // 复制栈顶对象,也压入栈中(StringBuilder对象)
aload_1; // 局部变量1压入栈中
invokestatic Method java/lang/String.valueOf:"(Ljava/lang/Object;)Ljava/lang/String;"; // 栈顶元素强转String
invokespecial Method java/lang/StringBuilder."<init>":"(Ljava/lang/String;)V"; // 弹出栈顶2个元素,调用StringBuilder构造函数,剩下一个刚才复制的、如今已经初始化好的StringBuilder在栈顶
iload_2; // 局部变量2压入栈中:数字0
invokevirtual Method java/lang/StringBuilder.append:"(I)Ljava/lang/StringBuilder;"; // 数字0 append进栈顶StringBuilder,返回值StringBuilder放回栈顶
ldc String "test"; // 常量"test"压入栈中
invokevirtual Method java/lang/StringBuilder.append:"(Ljava/lang/String;)Ljava/lang/StringBuilder;"; // “test”append进栈顶StringBuilder,返回值StringBuilder放回栈顶
invokevirtual Method java/lang/StringBuilder.toString:"()Ljava/lang/String;"; // 栈顶StringBuilder弹出,toString,String结果放入栈顶
astore_1; // String结果弹出放进局部变量1
iinc 2, 1; // 2号局部变量++
L35: stack_frame_type same;
iload_2; // 2号局部变量放到栈顶
bipush 10; // 常量10压栈,做为栈顶
if_icmplt L8; // 弹出栈中前两个数,若是栈中第二个数比栈顶元素小,就跳到第8行,不然继续往下
getstatic Field java/lang/System.out:"Ljava/io/PrintStream;"; // PrintStream对象压入栈顶
aload_1; // 局部变量1压入栈顶(字符串)
invokevirtual Method java/io/PrintStream.println:"(Ljava/lang/String;)V"; // 弹出栈中2个数,以栈中第二个元素做为参数1(即this),栈顶元素做为参数2,调用println方法
return;
}
复制代码
有点长,我稍微加了些注释以便阅读:可见javac仍然自动将循环体内的字符串拼接自动编译成了StringBuilder.append,可是请注意,new class java/lang/StringBuilder;
这条指令出如今了L8
标签和L35
后面的if_icmplt L8;
跳转指令之间,也就是说:它在循环体的内部 —— again也就是说 —— 这段代码被优化成了每次循环都会从新new一个StringBuilder作字符串拼接,循环多少次、就会建立多少个StringBuilder对象;虽说这可能并不是什么大不了的开销,但仍然仍是程序员手工把StringBuilder写在循环体外面、而后循环体内仅仅使用append更划算。因为现代的javac编译器可以自动将字符串拼接编译为StringBuilder.append的形式,所以平时直接书写'+'号拼接便可,但若须要经过一个循环来拼接字符串,则最好将StringBuilder显式地在循环体外建立再在循环体内使用,以免屡次重复建立StringBuilder对象java