对String反感的朋友千万别激动(鸡动),目的是为了纠正众多被书籍误导的朋友,
话说:很多书上都会推荐我们用String的时候不建议用连字符“+”来连接字符串,原因是效率过低,说是会创建很多的String对象,而用
StringBuilder(jdk5以后)、StringBuffer代替。我相信sun公司的人非常聪明,肯定会对使用如此频繁的String动一些手脚。
带着这个疑问仔细研究一下是否真的会创建很多String对象而导致低效,结果发现,在sun的jdk中这个说法是不成立的,其他jdk没有测试过,
sun公司的早就考虑到这个问题,String是开发过程中使用非常频繁,使用+连接字符串也是非常方便的,要知道java代码的编译成class文件的时候
,jvm都会对代码进行优化,从而提高代码质量.
我测试使用的是jdk1.6该例中会把System.out.println(a+b);
优化成System.out.println(new StringBuilder().apend(a).apend(b).toString())
由于StringBuilder是1.5以后才有,要指定jdk编译的版本号是1.5以后的才会优化成StringBuilder,1.5之前的版本都是优化成StringBuffer
使用javac编译的时候加个 -sourse 版本号 (例如1.5)   ,版本号不能低于1.2,之前不支持
可见用连字符“+”来连接字符串并没有给程序带来低效(说低效的人不知道能不能拿出证明!为什么传得全球满天飞,我真希望是自己理解错了)
源代码:
package bao;public class StringTest { public static void main(String[] args) {
String a="a";
String b="b";
System.out.println(a+b);
}}编译源代码(编译版本号:1.6),这个时候用StringBuilder代替
javac -source 1.6 StringTest.java
用javap 查看生成的字节码
执行
javap -verbose StringTest
如下class指令如果看不懂的话可以先看一下
http://blog.csdn.net/wzju64676266/archive/2010/09/10/5874703.aspx
java虚拟机规范  对字节码有详解Compiled from "StringTest.java"
public class bao.StringTest extends java.lang.Object
  SourceFile: "StringTest.java"
  minor version: 0
  major version: 50
  Constant pool:     //常量池
const #1 = Method       #11.#20;        //  java/lang/Object."<init>":()V
const #2 = String       #21;    //  a
const #3 = String       #22;    //  b
const #4 = Field        #23.#24;        //  java/lang/System.out:Ljava/io/PrintS
tream;
const #5 = class        #25;    //  java/lang/StringBuilder
const #6 = Method       #5.#20; //  java/lang/StringBuilder."<init>":()V
const #7 = Method       #5.#26; //  java/lang/StringBuilder.append:(Ljava/lang/S
tring;)Ljava/lang/StringBuilder;
const #8 = Method       #5.#27; //  java/lang/StringBuilder.toString:()Ljava/lan
g/String;
const #9 = Method       #28.#29;        //  java/io/PrintStream.println:(Ljava/l
ang/String;)V
const #10 = class       #30;    //  bao/StringTest
const #11 = class       #31;    //  java/lang/Object
const #12 = Asciz       <init>;
const #13 = Asciz       ()V;
const #14 = Asciz       Code;
const #15 = Asciz       LineNumberTable;
const #16 = Asciz       main;
const #17 = Asciz       ([Ljava/lang/String;)V;
const #18 = Asciz       SourceFile;
const #19 = Asciz       StringTest.java;
const #20 = NameAndType #12:#13;//  "<init>":()V
const #21 = Asciz       a;
const #22 = Asciz       b;
const #23 = class       #32;    //  java/lang/System
const #24 = NameAndType #33:#34;//  out:Ljava/io/PrintStream;
const #25 = Asciz       java/lang/StringBuilder;
const #26 = NameAndType #35:#36;//  append:(Ljava/lang/String;)Ljava/lang/String
Builder;
const #27 = NameAndType #37:#38;//  toString:()Ljava/lang/String;
const #28 = class       #39;    //  java/io/PrintStream
const #29 = NameAndType #40:#41;//  println:(Ljava/lang/String;)V
const #30 = Asciz       bao/StringTest;
const #31 = Asciz       java/lang/Object;
const #32 = Asciz       java/lang/System;
const #33 = Asciz       out;
const #34 = Asciz       Ljava/io/PrintStream;;
const #35 = Asciz       append;
const #36 = Asciz       (Ljava/lang/String;)Ljava/lang/StringBuilder;;
const #37 = Asciz       toString;
const #38 = Asciz       ()Ljava/lang/String;;
const #39 = Asciz       java/io/PrintStream;
const #40 = Asciz       println;
const #41 = Asciz       (Ljava/lang/String;)V;{
public bao.StringTest();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   return
  LineNumberTable:
   line 3: 0
public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=3, Args_size=1
   0:   ldc     #2; //String a     引用常量池 编号为#2的变量a
   2:   astore_1            //存储到局部变量表中index为1的位置
   3:   ldc     #3; //String b     引用常量池 编号为#3的变量b
   5:   astore_2        //存储到局部变量表中index为2的位置
   6:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;       //调用静态方法,引用常量池 编号为#4的变量PrintStream
   9:   new     #5; //class java/lang/StringBuilder       //创建StringBuilder
   12:  dup
   13:  invokespecial   #6; //Method java/lang/StringBuilder."<init>":()V   //调用构造方法
   16:  aload_1       //将局部变量表中index为1的变量压入到操作栈    其实保存的是a
   17:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;     //调用StringBuilder的apend方法
   20:  aload_2      //将局部变量表中index为2的变量压入到操作栈
   21:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;    //调用StringBuilder的apend方法
   24:  invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;     //调用StringBuilder的toString方法
   27:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V     //调用PrintStream的println方法
   30:  return
  LineNumberTable:
   line 9: 0
   line 10: 3
   line 11: 6
   line 12: 30
}
如上的代码是指定jdk是1.5版本编译出来的class文件,那现在来看一下用1.4以前版本编译的class文件吧
编译源代码(编译版本号:1.4),这个时候用StringBuffer代替,不同的地方就是把StringBuilder改成StringBuffer
javac -source 1.4 StringTest.java
用javap 查看生成的字节码
执行
javap -verbose StringTest
其他地方都相同的
看main方法的代码public static void main(java.lang.String[]);
  Code:
   Stack=3, Locals=3, Args_size=1
   0:   ldc     #2; //String a
   2:   astore_1
   3:   ldc     #3; //String b
   5:   astore_2
   6:   getstatic       #4; //Field java/lang/System.out:Ljava/io/PrintStream;
   9:   new     #5; //class java/lang/StringBuffer
   12:  dup
   13:  invokespecial   #6; //Method java/lang/StringBuffer."<init>":()V
   16:  aload_1
   17:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   20:  aload_2
   21:  invokevirtual   #7; //Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
   24:  invokevirtual   #8; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
   27:  invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   30:  return
  LineNumberTable:
   line 9: 0
   line 10: 3
   line 11: 6
   line 12: 30}
由此可见,jvm都做了优化,那为什么说用“+”会创建比StringBuilder、StringBuffer效率低呢?
这个说法源头是哪里来的

解决方案 »

  1.   

    看这样的例子:
    String a = 
    String b = 
    for(int i = 0; i < 1000; i++){
       a += b;
    }
    编译器会引入大量的StringBuilder变量
      

  2.   

    确实是会产生1000个StringBuilder
      

  3.   

    我的意思是书籍为什么不推荐使用String a="a";
    String b="b";
    String c="c";
    System.out.println(a+b+c);当然循环里面用"+"是不合适的
      

  4.   

    看不懂编译后的,不过stringbuffer应该是有优化
      

  5.   

    The Java Language Specification, Third Edition
    15.18.1.2 Optimization of String Concatenation
    An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.因为这种优化是否进行依赖于java编译器,所以也不能说是误导。简单情况下,一个非循环中的a+b都要使用StringBuilder手工优化的话,只能让代码恶心难看,的确没啥意思。一个很有意思的现象是,有些提法可能是某个人相当年随口说的,却被后面不断的初学者加以传播(比如blog转载),于是成为“经典”了。
      

  6.   

    并非说是就不能用加号 + 来连接字符串了,像string a="Hello ";
    string b="World";
    string c = a+b;当然直接用加号来连接了,主要是说的是在循环中不能去用加号连接字符串
    string a="Hello ";
    string b="World";
    for(int i = 0; i < 1000; i++){
      a += b; //这样会把中间的结果 toString() 成字符串,下次循环是又要 new StringBuilder(),再又 toString()
    }性能损耗是在大量的连接字符串的时候,而对连续的加号, string d = a+b+c; 编译器是会帮你优化成new StringBuilder(a).append(b).append(c).toString() 的。
      

  7.   


    这个明白,我不是指这个意思,我是指以前很多人以为
    string a="Hello ";
    string b="World";
    string c = a+b;会创建很多的String对象,很多人不知道其实是StringBuilder StringBuffer,认为效率底下,可能是某些书本上当时没有表达清楚,导致误解
      

  8.   

    StringBuilder bd = new StringBuilder
    for(){
       bd.append()
    }