Java - 什么时候用StringBuffer什么时候用StringBuilder?

本文深入解析Java中String、StringBuilder和StringBuffer的区别,重点探讨它们在运行速度和线程安全性方面的特性,以及在不同场景下的应用建议。
1. Java中的String,StringBuilder,StringBuffer三者的区别

这三个类之间的区别主要是在两个方面,即运行速度和线程安全这两方面。

运行速度
String str="abc";
System.out.println(str);
str=str+"de";
System.out.println(str);

如果运行这段代码会发现先输出“abc”,然后又输出“abcde”,好像是str这个对象被更改了,其实,这只是一种假象罢了,JVM对于这几行代码是这样处理的,首先创建一个String对象str,并把“abc”赋值给str,然后在第三行中,其实JVM又创建了一个新的对象也名为str,然后再把原来的str的值和“de”加起来再赋值给新的str,而原来的str就会被JVM的垃圾回收机制(GC)给回收掉了,所以,str实际上并没有被更改,也就是前面说的String对象一旦创建之后就不可更改了。所以,Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。

String str="abc"+"de";
StringBuilder stringBuilder=new StringBuilder().append("abc").append("de");
System.out.println(str);
System.out.println(stringBuilder.toString());

这样输出结果也是“abcde”和“abcde”,但是String的速度却比StringBuilder的反应速度要快很多,这是因为第1行中的操作和
String str=“abcde”;
是完全一样的,所以会很快,而如果写成下面这种形式

String str1="abc";
String str2="de";
String str=str1+str2;

那么JVM就会像上面说的那样,不断的创建、回收对象来进行这个操作了。速度就会很慢。

线程安全

在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的

  • 如果一个StringBuffer对象在字符串缓冲区被多个线程使用时,StringBuffer中很多方法可以带有synchronized关键字,所以可以保证线程是安全的,但StringBuilder的方法则没有该关键字,所以不能保证线程安全,有可能会出现一些错误的操作。所以如果要进行的操作是多线程的,那么就要使用StringBuffer,但是在单线程的情况下,还是建议使用速度比较快的StringBuilder。
2.引用知乎上的一个回复

作者:小猪
链接:https://www.zhihu.com/question/20101840/answer/164866159
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

我不知道为什么这个这么老的问题会出现在我的时间线上,看了一下回答,大多是2012,2013年的回答,照说那个年代,有些历史故事还很新鲜,却不知道为什么没有一个答案说到点子上。

stringbuffer固然是线程安全的,stringbuffer固然是比stringbuilder更慢,固然,在多线程的情况下,理论上是应该使用线程安全的stringbuffer的。

然而,然而,然而,有谁给我一个实际的案例来显示你需要一个线程安全的string拼接器?对不起,至少在我浅薄的十几年编程生涯中还没有遇到过,也许,仅仅是也许,这个地球上的确是存在这样的编程需求的,然而,它至少跟99.99…99%的程序员是无关的。所以,对于题主的问题,他们的适用场景是什么?最简单的回答是,stringbuffer基本没有适用场景,你应该在所有的情况下选择使用stringbuiler,除非你真的遇到了一个需要线程安全的场景,如果遇到了,请务必在这里留言通知我。

然后,补充一点,关于线程安全,即使你真的遇到了这样的场景,很不幸的是,恐怕你仍然有99.99…99%的情况下没有必要选择stringbuffer,因为stringbuffer的线程安全,仅仅是保证jvm不抛出异常顺利的往下执行而已,它可不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。

最后,为什么会有stringbuffer的存在,如果真的没有价值,为什么jdk会提供这个类?

答案太简单了,因为最早是没有stringbuilder的,sun的人不知处于何种愚蠢的考虑,决定让stringbuffer是线程安全的,然后大约10年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这10年之中这个愚蠢的决定为java运行速度慢这样的流言贡献了多大的力量,于是,在jdk1.5的时候,终于决定提供一个非线程安全的stringbuffer实现,并命名为stringbuilder。顺便,javac好像大概也是从这个版本开始,把所有用加号连接的string运算都隐式的改写成stringbuilder,也就是说,从jdk1.5开始,用加号拼接字符串已经没有任何性能损失了。

-----------补充一个小小的修改----------如诸多评论所指出的,我上面说,"用加号拼接字符串已经没有任何性能损失了"并不严谨,严格的说,如果没有循环的情况下,单行用加号拼接字符串是没有性能损失的,java编译器会隐式的替换成stringbuilder,但在有循环的情况下,编译器没法做到足够智能的替换,仍然会有不必要的性能损耗,因此,用循环拼接字符串的时候,还是老老实实的用stringbuilder吧。

3. 什么时候用StringBuffer什么时候用StringBuilder?
  • String:适用于少量的字符串操作的情况
  • StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况
  • StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
<think>嗯,用户问的是在Java中拼接字符串时应该用StringBuilder还是StringBuffer,以及它们的区别和适用场景。我需要先回忆这两个类的基本知识。记得StringBuilderStringBuffer都是可变的字符序列,而普通的String是不可变的。当需要频繁修改字符串时,使用这两个类比直接用String更高效,因为避免了创建大量临时对象。接下来,区别方面,最关键的应该是线程安全。StringBuffer是线程安全的,方法都加了synchronized关键字,所以适合多线程环境。而StringBuilder不是线程安全的,因此性能更好,适合单线程情况。这一点需要重点强调,因为用户可能关心性能和线程安全之间的权衡。然后,适用场景的话,比如在单线程下拼接字符串,比如循环里面,用StringBuilder更高效。如果是多线程,比如有多个线程同时操作同一个字符串缓冲区,这时候就得用StringBuffer了。不过现在很多情况下,即使是在多线程环境中,可能也会用其他同步机制而不是StringBuffer,所以需要结合实际例子说明。还要提到它们的API几乎相同,所以切换使用起来比较方便。另外,性能差异主要来自同步的开销,所以在高并发场景下StringBuilder可能更快,但线程不安全的问题需要注意。用户可能还关心什么时候该用哪个,比如在常见的单线程应用里,比如Android开发或者普通的Java应用,大部分时候推荐用StringBuilder。而像Web应用中的某些多线程处理可能需要StringBuffer,不过这种情况可能较少,现在更常用的是局部变量中使用StringBuilder,避免线程安全问题。 另外,可能需要提到Java版本的问题,StringBuilderJava5引入的,而StringBuffer更早。所以如果还在用老版本的话可能需要注意兼容性,但现在大部分项目应该已经升级到Java5以上了。还要举例子说明,比如在循环中拼接字符串,使用StringBuilder的append方法,最后toString。对比如果使用String的话,每次拼接都会生新对象,导致性能下降和内存浪费。 总结部分要明确两者的主要区别,以及推荐的使用场景,帮助用户根据具体情况选择。可能还需要提醒用户,虽然StringBuilder性能更好,但线程安全不可忽视,尤其是在多线程环境下必须谨慎选择。最后,生的相关问题需要考虑用户可能进一步想了解的内容,比如性能差异的具体数据,或者如何在实际项目中决定使用哪一个,或者是否有其他替代方案等。</think>### Java字符串拼接选择StringBuilderStringBuffer的对比分析 #### 一、核心区别 1. **线程安全性** - **StringBuffer**:所有方法均通过`synchronized`关键字实现线程安全,适合多线程环境中的字符串操作[^2][^3] - **StringBuilder**:未实现线程同步,性能更高但仅适用于单线程场景[^2][^3] 2. **性能表现** 在单线程环境下,**StringBuilder**的字符串拼接速度比StringBuffer快约 **10-15%**(因省去同步锁开销)[^4] #### 二、使用场景对比 | 场景类型 | 推荐选择 | 原因说明 | |------------------|-------------------|--------------------------------------------------------------------------| | 单线程循环拼接 | StringBuilder | 例如日志处理、本地数据组装等场景 | | 多线程共享缓冲区 | StringBuffer | 如Web应用的全局字符串缓存(需注意现代框架中更常用局部变量+线程隔离技术) | | 临时字符串操作 | 两者均可 | 但优先选择StringBuilder以保持代码简洁性 | #### 三、典型代码示例 ```java // 单线程场景示例 StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append(i).append(","); // 无需同步锁 } String result = sb.toString(); // 多线程场景示例(需谨慎设计) StringBuffer sBuffer = new StringBuffer(); ExecutorService executor = Executors.newFixedThreadPool(4); for (int i = 0; i < 4; i++) { executor.submit(() -> { synchronized(sBuffer) { // 仍需显式同步 sBuffer.append(Thread.currentThread().getName()); } }); } ``` #### 四、选择建议 1. **优先选择StringBuilder**:现代Java开发中约 **90%** 的字符串拼接场景均为单线程操作[^3] 2. **避免过早优化**:仅在确实存在多线程共享需求时才使用StringBuffer 3. **注意代码规范**:在方法内部始终使用StringBuilder,避免将其作为类员变量暴露多线程风险 ---
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qumy97

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值