我可以用两种方式实现单一方法,
1.
public String getTestMessage()
{
return "Hello" + "World..!";
}
2.
public String getTestMessage()
{
return new StringBuffer("Hello").append("World..!").toString();
}
在第一种情况下,将创建两个新的 String
对象。
在第二种情况下,也会创建两个新对象,但会有一个 String
和 StringBuffer
。现在他们的方式会很快吗?我有点困惑。
最佳答案
在您的两个场景之间,选项 1 每次都会更快,除非 JITC 做了一些我不期望的事情。不过,我真的不希望它有什么不同,除非您非常频繁地调用这些方法。
为什么?因为您实际上不使用选项 1 创建新对象。编译器应该执行常量折叠,转为 "Hello" + "World..!"
进入"HelloWorld..!"
.因为这是一个编译时常量 String
, 它会自动驻留在 String
中VM 启动时的池。因此,每次调用该方法时,您只会获得对该“规范”String
的引用。 .不执行任何对象创建。
在选项 2 中,您总是创建多个对象 -- StringBuffer
(顺便说一句,你应该使用 StringBuilder
),支持 char[]
, 结果 String
(至少)。在一个紧密的循环中这样做并不是很有效。
此外,选项 1 的可读性更高,这始终是您在编写代码时应该考虑的问题。
证明:
给定这个测试代码:
public class Test {
public String getTestMessageA() {
return "Hello" + "World..!";
}
public String getTestMessageB() {
return new StringBuffer("Hello").append("World..!").toString();
}
}
使用 javac -XD-printflat
编译向我们展示了这段代码在编译为字节码之前被处理成什么:
public class Test {
public Test() {
super();
}
public String getTestMessageA() {
return "HelloWorld..!";
}
public String getTestMessageB() {
return new StringBuffer("Hello").append("World..!").toString();
}
}
注意如何 "Hello" + "World..!"
在编译时被转换为单个 String
.所以String
串联不是第一个选项中发生的事情。
现在让我们看看字节码。这是常量池:
Constant pool:
#1 = Methodref #10.#20 // java/lang/Object."<init>":()V
#2 = String #21 // HelloWorld..!
#3 = Class #22 // java/lang/StringBuffer
#4 = String #23 // Hello
#5 = Methodref #3.#24 // java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
#6 = String #25 // World..!
#7 = Methodref #3.#26 // java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
#8 = Methodref #3.#27 // java/lang/StringBuffer.toString:()Ljava/lang/String;
这是选项 1 的字节码:
public java.lang.String getTestMessageA();
Code:
0: ldc #2 // String HelloWorld..!
2: areturn
好吧,这很短。如您所见,JVM 从池中加载一个常量 ( ldc
) 并将其返回。没有直接在方法中创建对象。
现在这是选项 2 的字节码:
public java.lang.String getTestMessageB();
Code:
0: new #3 // class java/lang/StringBuffer
3: dup
4: ldc #4 // String Hello
6: invokespecial #5 // Method java/lang/StringBuffer."<init>":(Ljava/lang/String;)V
9: ldc #6 // String World..!
11: invokevirtual #7 // Method java/lang/StringBuffer.append:(Ljava/lang/String;)Ljava/lang/StringBuffer;
14: invokevirtual #8 // Method java/lang/StringBuffer.toString:()Ljava/lang/String;
17: areturn
因此这段代码创建了一个新的 StringBuffer
, 从字符串池中加载适当的常量,调用 append()
方法,然后调用 toString()
缓冲区上的方法。 toString()
是这样实现的:
@Override
public synchronized String toString() {
if (toStringCache == null) {
toStringCache = Arrays.copyOfRange(value, 0, count);
}
return new String(toStringCache, true);
}
所以选项2会在你每次调用的时候创建两个新的对象,同时也会执行更多的指令。因此,选项 1 会更快。
关于java - Java中StringBuffer和String的使用场景,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24457626/