StringBuilder vs StringBuffer:Java中的10个主要区别

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

StringBuilder vs StringBuffer:如何选择以及为什么这很重要?

\n\n

如果你曾经编写过大量处理字符串的Java代码(例如,拼接SQL查询、生成JSON或只是将几十个文本片段连接起来),那么你很可能遇到过这个两难选择:StringBuilder还是StringBuffer?在Stack Overflow上,这个问题已经积累了数十万次浏览,这并非没有原因。乍一看,这两个类就像双胞胎一样相似——它们都继承自AbstractStringBuilder,都使用可变的字符数组,都支持append()、insert()、delete()方法。但正如常言所说,魔鬼藏在细节中。

\n\n

在本文中,我们不仅仅会列举它们的区别。我们将通过实际示例进行深入分析,探究JVM的内部机制,并回答你在面试中一定会被问到的问题。让我们开始吧!

\n\n

1. 主要区别:同步

\n\n

这是最重要的一点,也是所有问题的根源。StringBuffer是线程安全的。它几乎所有公共方法都使用synchronized关键字进行了同步。StringBuilder则没有同步。仅此而已。

\n\n

让我们看一下代码(来自真实JDK的简化版本):

\n\n
// StringBuffer (Java 17)\npublic synchronized StringBuffer append(String str) {\n    toStringCache = null;\n    super.append(str);\n    return this;\n}\n\n// StringBuilder\npublic StringBuilder append(String str) {\n    super.append(str);\n    return this;\n}\n
\n\n

一行synchronized——这就是全部区别。但这行代码改变了一切:性能、应用场景,甚至应用程序的架构。

\n\n

何时使用StringBuffer?

\n
    \n
  • 你在编写多线程应用程序。
  • \n
  • 同一个StringBuffer实例在没有外部同步的情况下被多个线程使用。
  • \n
  • 示例:一个日志记录器,它从不同线程收集消息到一个缓冲区中。
  • \n
\n\n

何时使用StringBuilder?

\n
    \n
  • 单线程代码(90%的情况)。
  • \n
  • 方法内部的局部变量。
  • \n
  • 字符串拼接的循环(编译器本身会建议使用StringBuilder)。
  • \n
\n\n

2. 性能:谁更快?

\n\n

这一点是显而易见的:StringBuilder比StringBuffer更快。快多少?取决于负载,但平均快2-5倍。让我们通过一个简单的基准测试来验证。

\n\n
public class PerformanceTest {\n    private static final int ITERATIONS = 10_000_000;\n\n    public static void main(String[] args) {\n        // StringBuilder\n        long start = System.nanoTime();\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < ITERATIONS; i++) {\n            sb.append("a");\n        }\n        long sbTime = System.nanoTime() - start;\n\n        // StringBuffer\n        start = System.nanoTime();\n        StringBuffer sbf = new StringBuffer();\n        for (int i = 0; i < ITERATIONS; i++) {\n            sbf.append("a");\n        }\n        long sbfTime = System.nanoTime() - start;\n\n        System.out.println("StringBuilder: " + sbTime / 1_000_000 + " ms");\n        System.out.println("StringBuffer:  " + sbfTime / 1_000_000 + " ms");\n        System.out.println("Buffer慢 " + (sbfTime / (double) sbTime) + " 倍");\n    }\n}\n
\n\n

在我的笔记本电脑上(Intel i7, 16GB RAM, Java 17),结果很稳定:StringBuilder ~180 ms,StringBuffer ~620 ms。差距接近3.5倍。如果加上实际工作(解析、格式化),差距可能会更大。为什么?因为每次调用synchronized都涉及监视器检查、锁获取和释放。在单线程代码中,这纯粹是浪费时间。

\n\n
\n 重要提示: 如果你的方法保证

博客

书籍推荐