这是书籍的误导吗?

wzju64676266 2010-10-11 01:15:31

对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效率低呢?
这个说法源头是哪里来的

...全文
205 11 打赏 收藏 转发到动态 举报
写回复
用AI写文章
11 条回复
切换为时间正序
请发表友善的回复…
发表回复
wzju64676266 2010-10-11
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 kypfos 的回复:]
并非说是就不能用加号 + 来连接字符串了,像

string a="Hello ";
string b="World";
string c = a+b;

当然直接用加号来连接了,主要是说的是在循环中不能去用加号连接字符串


string a="Hello ";
string b="World";
for(int i = 0; i < 1000; i++){
a +=……
[/Quote]

这个明白,我不是指这个意思,我是指以前很多人以为
string a="Hello ";
string b="World";
string c = a+b;

会创建很多的String对象,很多人不知道其实是StringBuilder StringBuffer,认为效率底下,可能是某些书本上当时没有表达清楚,导致误解
lijiemissfei 2010-10-11
  • 打赏
  • 举报
回复
学习了。
Yanbin_Q 2010-10-11
  • 打赏
  • 举报
回复
并非说是就不能用加号 + 来连接字符串了,像

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() 的。

wzju64676266 2010-10-11
  • 打赏
  • 举报
回复
[Quote=引用 5 楼 zangxt 的回复:]
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 t……
[/Quote]

赞同这种说法
ZangXT 2010-10-11
  • 打赏
  • 举报
回复
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转载),于是成为“经典”了。

24K純帥 2010-10-11
  • 打赏
  • 举报
回复
看不懂编译后的,不过stringbuffer应该是有优化
salever 2010-10-11
  • 打赏
  • 举报
回复
并非误导,而是很多人不加思考就滥用滥说而已

除非真正遇到性能问题,否则不要过多考虑性能、效率,简单的往往是最好的
wzju64676266 2010-10-11
  • 打赏
  • 举报
回复
我的意思是书籍为什么不推荐使用

String a="a";
String b="b";
String c="c";
System.out.println(a+b+c);



当然循环里面用"+"是不合适的
wzju64676266 2010-10-11
  • 打赏
  • 举报
回复
确实是会产生1000个StringBuilder
ZangXT 2010-10-11
  • 打赏
  • 举报
回复
看这样的例子:
String a =
String b =
for(int i = 0; i < 1000; i++){
a += b;
}
编译器会引入大量的StringBuilder变量
pywepe 2010-10-11
  • 打赏
  • 举报
回复
StringBuilder bd = new StringBuilder
for(){
bd.append()
}
Google图书搜索解决方案受到了广泛关注。 为了回应反对,原定的和解已被推迟,现在将重新提交。 在这篇简短的论文中,我要讲三点。 首先,我对案件中的竞争问题进行了快速状态更新。 其次,我要谈谈关于竞争问题的评论中出现的一个关键问题,即,为评估诸如GBS之类的新安排是否具有竞争性而制定竞争政策基线的正确方法是什么? 这个问题对于反托拉斯和创新政策的交集是普遍感兴趣的,并且鉴于这两者对经济健康的重要性,至关重要的是我们必须弄清基线问题。 如果我们仅跟踪输出的扩展,就会被误导。 聪明的卡特尔主义者希望在婴儿期就对新产业进行卡特尔化,因为他们知道新产品创新将不可避免地提高产量,即使它的增长幅度远低于面对全面竞争时的水平。 如果创新者知道仅根据创新前的基准对其进行评估,他们将希望将反竞争功能与竞争功能捆绑在一起。 第三,反托拉斯执法人员在应用于Google图书搜索解决方案本身时,需要从反竞争功能中区分项目的真正利益。 显然,这是反托拉斯法中的一个常规问题,但这意味着产品创新不能用作抵制标准反托拉斯法的一般方法。 诸如数字化图书扫描之类的单个基础结构可用于同时提供许多产品,并且一种产品的竞争优势无法使使用相同基础结构的第二种产品的反竞争步骤得以隔离。

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧