Java: why the body's circulation to change the string concatenation worth StringBuilder.append?

Two code examples:

  1. Example 1:
    public static void main(String[] args) {
    	String testStr = "test";
    	String rst = testStr + 1 + "a" + "pig" + 2;
    	System.out.println(rst);
    }
    复制代码
  2. Example 2:
    public static void main(String[] args) {
    	String testStr = "";
    	for (int i = 0; i < 10; i++) {
    		testStr += i + "test";
    	}
    	System.out.println(testStr);
    }
    复制代码

analysis:

  1. With javap decompile 例1obtain the following bytecode class file code represents:
    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 automatically seen such a continuous string concatenation compiled into StringBuilder.append, and therefore do not have to deliberately written StringBuilder.append form when writing code;
  2. With javap decompile 例2obtain the following bytecode class file code represents:
    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;
    	
    }
    复制代码
    A little long, a little I added some comments to read: javac still visible automatically cycle the body's string concatenation automatically compiled into StringBuilder.append, but please note that new class java/lang/StringBuilder;this directive appears in the L8label and L35back if_icmplt L8;between the jump instruction , that is to say: it circulates inside the body - again that is - the code is optimized for each cycle will again become a new StringBuilder do string concatenation, the number of loops, the number of StringBuilder object is created ; although this may not be a big deal of overhead, but it is still a programmer to write by hand the StringBuilder outside the loop, then the loop body using only append more cost-effective.

to sum up

Since modern javac compiler can automatically compiled to form StringBuilder.append string concatenation, and so usually the direct writing '+' sign to splice, but if the string needs to be spliced ​​through a cycle, preferably the explicit StringBuilder then create circulation in vivo use, in order to avoid repeated StringBuilder object created in vitro cycle

Guess you like

Origin juejin.im/post/5d3b265ff265da1b971abbad