StringBuilder vs 字符串拼接在 toString() 中:底层工作原理

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

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

开始课程

StringBuilder vs 字符串拼接在 toString() 中:底层工作原理

\n\n

您是否曾想过,为什么在 Stack Overflow 上关于如何在 Java 中正确编写 toString() 的争论至今仍在继续?一些开发者坚信 StringBuilder 是唯一正确的途径。另一些人则轻松地使用普通的加号拼接,毫不担心。谁才是对的?

\n\n

让我们深入 Java 编译器和 JIT(即时)优化器的底层。您将了解到,当您编写 str1 + str2 时实际发生了什么,以及为什么在 2024 年这个问题的答案可能会让您感到惊讶。

\n\n

为什么这个话题如此热门?

\n\n

“StringBuilder vs String concatenation in toString()”这个问题在 Stack Overflow 上获得了数千次浏览,这并非偶然。每个 Java 开发者都至少编写过一次 toString() 方法。并且每个人都面临过选择:是通过加号优雅地编写,还是通过 StringBuilder“正确”地编写。

\n\n

关于 Java 字符串拼接 总是很慢的误解已经存在了 20 多年。在 Java 1.4 和 Java 5 时代,这确实是事实。但编译器并没有停滞不前。

\n\n

Java 规范怎么说?

\n\n

根据 Java 语言规范(JLS §15.18.1),字符串的 + 运算符是语法糖。编译器必须将字符串拼接转换为高效的代码。从 Java 5(J2SE 5.0)开始,javac 编译器会自动将拼接替换为使用 StringBuilder。

\n\n

让我们在实践中验证这一点。

\n\n

代码示例:简单的 toString()

\n\n

我们来看一个经典示例——Person 类的 toString() 方法:

\n\n
public class Person {\n    private String name;\n    private int age;\n    private String email;\n\n    @Override\n    public String toString() {\n        return "Person{" +\n                "name='" + name + '\\'' +\n                ", age=" + age +\n                ", email='" + email + '\\'' +\n                '}';\n    }\n}\n
\n\n

看起来简洁且可读。但在字节码中是什么样子呢?

\n\n

深入字节码

\n\n

编译这个类,并使用 javap -c Person.class 查看字节码:

\n\n
public java.lang.String toString();\n    Code:\n       0: new           #7    // class java/lang/StringBuilder\n       3: dup\n       4: invokespecial #8    // Method StringBuilder."<init>":()V\n       7: ldc           #9    // String "Person{name='"\n       9: invokevirtual #10   // StringBuilder.append(String)\n      12: aload_0\n      13: getfield      #3    // Field name:Ljava/lang/String;\n      16: invokevirtual #10   // StringBuilder.append(String)\n      19: ldc           #11   // String "', age="\n      21: invokevirtual #10   // StringBuilder.append(String)\n      24: aload_0\n      25: getfield      #4    // Field age:I\n      28: invokevirtual #12   // StringBuilder.append(int)\n      31: ldc           #13   // String ", email='"\n      33: invokevirtual #10   // StringBuilder.append(String)\n      36: aload_0\n      37: getfield      #5    // Field email:Ljava/lang/String;\n      40: invokevirtual #10   // StringBuilder.append(String)\n      43: ldc           #14   // String "'}"\n      45: invokevirtual #10   // StringBuilder.append(String)\n      48: invokevirtual #15   // StringBuilder.toString():String\n      51: areturn\n
\n\n

看到了吗?编译器自己创建了 StringBuilder! 它创建了一个新的 StringBuilder 对象,为字符串的每个部分调用 append(),然后调用 toString()。这与您手动编写的代码相同,但源代码更具可读性。

\n\n

那么显式使用 StringBuilder 呢?

\n\n

现在,我们编写一个显式使用 StringBuilder 的版本:

\n\n
@Override\npublic String toString() {\n    Strin                

博客

书籍推荐