4

我知道 String 是不可变的,以及何时使用 StringBuilder 或 StringBuffer。我还读到这两个片段的字节码最终会是相同的:

//Snippet 1
String variable = "text";
this.class.getResourceAsStream("string"+variable);

//Snippet 2
StringBuilder sb = new StringBuilder("string");
sb.append("text");
this.class.getResourceAsStream(sb.toString());

但我显然有问题。在 Eclipse 中通过 Snippet 1 进行调试时,我实际上被带到了 StringBuilder 构造函数和 append 方法。我想我缺少有关如何解释字节码以及调试器如何引用源代码中的行的详细信息;如果有人能解释一下,我真的很感激。另外,也许您可​​以指出什么是 JVM 特定的,什么不是(例如,我正在运行 Oracle 的 v6),谢谢!

4

2 回答 2

8

为什么调试String拼接时会弹出StringBuilders?

因为字符串连接(通过“+”运算符)通常编译为使用 aStringBufferStringBuilder进行连接的代码。JLS 明确允许这种行为。

“实现可以选择在一个步骤中执行转换和连接,以避免创建然后丢弃中间字符串对象。为了提高重复字符串连接的性能,Java 编译器可以使用 StringBuffer 类或类似技术来减少通过评估表达式创建的中间字符串对象。” JLS 15.18.1

(如果您的代码使用 aStringBuffer而不是 a StringBuilder,可能是因为它是使用非常旧的 Java 编译器编译的,或者因为您指定了非常旧的目标 JVM。StringBuilder该类是对 Java 的相对补充。旧版本的 JLS用来StringBuffer代替StringBuilder,IIRC。)


此外,也许您可​​以指出哪些是 JVM 特定的,哪些不是。

生成的字节码"string" + variable"取决于 Java 编译器如何处理连接。(事实上​​,所有生成的字节码在某种程度上都依赖于 Java 编译器。JLS 和 JVM 规范没有规定必须生成哪些字节码。规范更多的是关于程序应该如何表现,以及单个字节码的作用。)


@supercat 评论:

我想知道为什么字符串连接不会使用例如接受两个字符串对象、分配适当组合大小的缓冲区并将它们连接起来的字符串构造函数重载?或者,当连接更多字符串时,需要一个 String[] 的重载?创建一个包含对要连接的字符串的引用的 String[] 应该不会比创建一个 StringBuilder 更昂贵,并且能够一次性创建一个完美大小的后备存储应该是一个简单的性能胜利。

也许......但我会说可能不是。这是一个涉及复杂权衡的复杂领域。为字符串连接选择的实施策略必须在各种不同的用例中都能很好地工作。

我的理解是,最初的策略是在查看了许多方法之后选择的,并进行了一些大规模的静态代码分析和基准测试,以试图找出哪种方法最好。我他们考虑了您提出的所有替代方案。(毕竟,他们是/是聪明人......)

话虽如此,Java 6、7 和 8 的完整源代码库可供您使用。这意味着您可以下载它,并尝试自己的一些实验,看看您的理论是否正确。如果它们是……并且您可以收集确凿的证据证明它们是……然后向 OpenJDK 团队提交补丁。

于 2012-08-05T09:18:27.243 回答
1

@StephenC我仍然不相信这个解释。编译器可能会做它想做的任何优化,但是当您通过 Eclipse 进行调试时,源代码视图对编译器代码是隐藏的,它不应该将一段代码跳转到同一源文件中的另一段代码。

问题中的以下描述表明源代码和字节码不同步。即,他没有运行最新的代码。

When debugging through Snippet 1 in eclipse, I am actually taken to the StringBuffer constructor and to the append method

how the debugger refers back to the lines in the source code

于 2012-08-05T09:31:37.370 回答