参考资料
羞,Java 字符串拼接竟然有这么多姿势
前段时间上课的时候记得威哥讲到了关于StringBuilder的优势
(尽力还原,并非原话)
如下代码
String a="a"; String b="b"; String c="c"; System.out.println(a+b+c);
威哥对代码实际画图大概是这样的
(注意这是错误结论!)
用代码来解释就是
"a"+"b"+"c"; 的实质是
new StringBuilder(new StringBuilder("a").append("b").toString()).append("c").toString();
使用 "+" 拼接字符串 每一步都会在常量池创建一个新的字符串用来和后面的字符串拼接
虽然说String的拼接的确有时候效率很低,但是不至于内部代码睿智到这种程度
这句话在某些情况某些角度来讲的确是对的!
==========================以上是错误结论================================
所以威哥推荐我们在拼接字符串的时候不要使用
"a"+"b"+"c"这种形式
而使用new StringBuilder().append("a").append("b").append("c").toString();
然而这两种代码真的有区别嘛?
我写出以下代码测试
String a="a"; String b="b"; String c="c"; System.out.println(a+b+c); System.out.println(new StringBuilder().append(a).append(b).append(c).toString());
导出编译成jar包后放入JAD反编译查看源码( IDEA我真的是不会用,导出搞了半天 )
然后得到了如下睿智的代码
String a = "a"; String b = "b"; String c = "c"; System.out.println(a + b + c); System.out.println(a + b + c);
于是乎,放弃JAD改用java编译器自带的反编译
javap -c 对代码进行反汇编
得到了这段代码
Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."
":()V 4: return public static void main(java.lang.String[]); Code: 0: ldc #2 // String a 2: astore_1 3: ldc #3 // String b 5: astore_2 6: ldc #4 // String c 8: astore_3 9: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 12: new #6 // class java/lang/StringBuilder 15: dup 16: invokespecial #7 // Method java/lang/StringBuilder."
":()V 19: aload_1 20: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 23: aload_2 24: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 27: aload_3 28: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 37: getstatic #5 // Field java/lang/System.out:Ljava/io/PrintStream; 40: new #6 // class java/lang/StringBuilder 43: dup 44: invokespecial #7 // Method java/lang/StringBuilder." ":()V 47: aload_1 48: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 51: aload_2 52: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 55: aload_3 56: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 59: invokevirtual #9 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 62: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 65: return }
左边是一大段的汇编代码看不懂,整理一下主要代码如下
// String a // String b // String c // Field java/lang/System.out:Ljava/io/PrintStream; // class java/lang/StringBuilder // Method java/lang/StringBuilder."":()V // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Method java/lang/StringBuilder.toString:()Ljava/lang/String; // Method java/io/PrintStream.println:(Ljava/lang/String;)V // Field java/lang/System.out:Ljava/io/PrintStream; // class java/lang/StringBuilder // Method java/lang/StringBuilder." ":()V // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; // Method java/lang/StringBuilder.toString:()Ljava/lang/String; // Method java/io/PrintStream.println:(Ljava/lang/String;)V
是的,上下两段代码是完全一样的
生成一个StringBuilder对象,调用空参构造方法,然后执行三次append之后然后toString()
所以得出结论
"a"+"b"+"c"的代码最终被翻译成了new StringBuilder().append("a").append("b").append("c").toString();
根本不存在中间创建一个新的StringBuilder拼接字符串一说
那么StringBuilder相对于String之间的"+"拼接根本优势在于哪里?
这里参考了一篇CSDN关于字符串拼接讲的非常详解的文章
通俗的说一下大拿的讲解
String的"+"操作符比较适用于在一句话中进行的字符串拼接操作
而不适合在一个循环体结构中不断的拼接字符串,因为每一次for循环都会创建一个新的StringBuilder而
使用StringBuilder可以在循环体外面生成一个StirngBuilder然后每次只需要对相同的StringBuilder对象进行append操作
为什么阿里巴巴不建议在for循环中使用”+”进行字符串拼接
实际操作一下有了如下代码
Runtime runtime=Runtime.getRuntime(); runtime.gc();; String a=""; long startMemory=0; for(int i=0;i<9999;i++) { a+="a"; } long endMemory=runtime.freeMemory(); System.out.println("a: "+(endMemory-startMemory)); String b=""; StringBuilder builder=new StringBuilder(); runtime.gc(); startMemory=runtime.freeMemory(); for(int i=0;i<9999;i++) { builder.append("b"); } endMemory=runtime.freeMemory(); System.out.println("b: "+(endMemory-startMemory));
实时查看一下JVM的freeMemory
注意两次计算间要用gc清理一下内存 (实际计算结果可能与操作系统环境有小偏差)
调用结果如下
a: 14177064 b: -183360
这里笔者也不太明白为什么第二个计算结果会是一个负数
不过结果显而易见 第一种"+"的拼接操作消耗了大量内存用于创建StringBuilder对象,用完就扔,无引用指向,留着浪费内存
综上所述
平时尽量能用StringBuilder(StringBuffer)就用,String的"+"可能更像是ENUM一样java给提供的语法糖.
by rzYork
如果有误 欢迎指错