Java 为什么调试字符串连接时会弹出StringBuilder?

Java 为什么调试字符串连接时会弹出StringBuilder?,java,debugging,bytecode,Java,Debugging,Bytecode,我知道字符串是不可变的,并决定何时使用StringBuilder或StringBuffer。我还了解到,这两个代码片段的字节码最终是相同的: //Snippet 1 String variable = "text"; this.class.getResourceAsStream("string"+variable); //Snippet 2 StringBuilder sb = new StringBuilder("string"); sb.append("text"); this.class

我知道字符串是不可变的,并决定何时使用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中通过代码段1进行调试时,我实际上被带到了StringBuilder构造函数和append方法。我想我缺少关于字节码如何解释以及调试器如何引用源代码中的行的详细信息;如果有人能解释一下,我会非常感激。另外,也许您可以指出哪些是JVM特有的,哪些不是(例如,我正在运行Oracle的v6),谢谢

为什么调试字符串连接时会弹出StringBuilder

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

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

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


另外,也许您可以指出哪些是JVM特有的,哪些不是

“string”+variable“
生成的字节码取决于Java编译器处理串联的方式。(事实上,所有生成的字节码在某种程度上都依赖于Java编译器。JLS和JVM规范没有规定必须生成哪些字节码。规范更多地是关于程序应该如何运行以及各个字节码的作用。)


@超级猫评论:

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

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

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


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

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

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

在eclipse中通过Snippet 1进行调试时,我实际上被带到了StringBuffer构造函数和append方法


调试器如何引用源代码中的行

您确定正在运行更新的编译代码吗?一个可能的原因可能是您正在查看最新的源代码,但正在运行以前编译的代码等效代码(假设
javac
)实际上是
String variable=“text”;this.class.getResourceAsStream(新的StringBuilder().append(“字符串”).append(变量).toString());
在eclipse中通过代码片段1进行调试时,这与
有什么关系?我实际上被带到了StringBuffer构造函数和append方法
。您能澄清一下吗?让我重复并澄清一下:您的意思是,由于JLS允许,字符串中的+运算符实际上被转换为StringBuilder然后整个结果代码被编译成字节码。对吗?@Miquel-嗯,没有“第一个”…但是javac编译器正在使用StringBuilder发出进行字符串连接的字节码…哎呀。很抱歉,这需要我使用StringBuilder而不是StringBuffer。修复了原来的问题。好的,我假设它会跳转到同一源文件中的代码的其他部分,在那里他再次使用StringBuffer。如果“+”向他显示Javas StringBuilder的append(不是源代码中的append)那么它是有意义的。您可能会发现我的解释不可信,但我可以向您保证,这就是实际发生的情况。您可以通过使用“.class”来确认这一点文件并使用
javap
检查字节码。您将看到对StringBuilder构造函数和其他方法的调用。Eclipse调试器只是报告实际发生的调用。这不是