62,615
社区成员
发帖
与我相关
我的任务
分享
对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效率低呢?
这个说法源头是哪里来的