介绍
Java中常用字符串处理类包括 String、StringBuffer 和 StringBuilder ,当对字符串进行修改的时候,使用 StringBuffer 和 StringBuilder 一般比使用 String 效率更高。因为 StringBuffer 的内部实现方式和 String 不同,StringBuffer 在进行字符串处理时,不生成新的对象,在速度和内存使用上都要优于 String。
String
String 是被 final 关键字修饰的,它是不可变的,就算调用 String 的 concat 方法,那也只是把字符串拼接起来并重新创建一个新对象,把拼接后的 String 的值赋给新创建的对象。
StringBuffer
StringBuffer 是可变的,调用StringBuffer 的 append 等方法,来改变 StringBuffer ,相比较于 String 创建新对象,StringBuffer 的 append 等方法直接在原对象上操作,不新建对象,是更高效的,所以如果需要对字符串进行修改推荐使用 StringBuffer。
StringBuilder
StringBuilder 在 Java 5 中被提出,它拥有与 StringBuffer 几乎相同的方法与用法,但是无论哪方面都完胜 StringBuilder。
StringBuffer vs StringBuilder
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder,在 AbstractStringBuilder 类中也是使用字符数组保存字符串。StringBuffer 很多方法都是 synchronized 修饰的,所以我们常说 StringBuffer 是线程安全的,官方的注释也写着 StringBuffer 是一个线程安全的可变的字符序列,StringBuffer 可以安全的在多线程场景下使用,但事实并非如此。
事实上,StringBuffer 不是线程安全的,而且效率低下,任何出现 StringBuffer 的地方都可以用 StringBuilder 去替换。道理很简单, append 等方法在多线程环境下,它能保证程序运行结果的正确性和一致性吗?试想一下,使用 append 拼接 SQL,在多线程环境运行,你根本无法预测最终结果,不光无法预测,JVM 自己都不确定最终结果。
StringBuffer 的线程安全,仅仅是保证 JVM 不抛出异常顺利的往下执行而已,它不保证逻辑正确和调用顺序正确。大多数时候,我们需要的不仅仅是线程安全,而是锁。事实上,你根本找不到一个用 StringBuffer 的理由,《Effective java》的作者 Joshua Bloch 在书中说:
原文:StringBuffer instances are almost always used by a single thread, yet they perform internal synchronization. It is for this reason that StringBuffer was supplanted by StringBuilder, which is just an unsynchronized StringBuffer.
译文:StringBuffer 实例几乎总是由单个线程使用,但它们执行内部同步。正是由于这个原因,StringBuffer 被 StringBuilder 取代,后者只是一个不同步的StringBuffer。
为什么会有 StringBuffer 的存在呢?如果真的没有价值,为什么 JDK 会提供这个类?
因为最早是没有 StringBuilder 类的,sun 公司的人错误地决定让 StringBuffer 是线程安全的,然后大约 10 年之后,人们终于意识到这是一个多么愚蠢的决定,意识到在这 10 年之中这个愚蠢的决定为 Java 运行速度慢这样的流言贡献了多大的力量,于是,在 JDK1.5 的时候,终于决定提供一个非线程安全的 StringBuffer 实现,并命名为 StringBuilder 。
总结
类 | 线程安全 | 性能(一般情况) | 使用环境 |
---|---|---|---|
String | 是 | 低 | 少量数据 |
StringBuffer | 否 | 低 | 单线程环境 |
StringBuilder | 否 | 高 | 单线程环境 |
StringBuffer 基本没有适用场景,你应该在所有的情况下坚定地选择使用 StringBuilder。