Java中的StringBuffer和String使用场景

时间:2014-06-27 17:28:15

标签: java performance core

我可以用两种方式实现单一方法,

1

public String getTestMessage()
{
    return "Hello" + "World..!";
}

2

public String getTestMessage()
{
    return new StringBuffer("Hello").append("World..!").toString();
}

在第一个场景中,将创建两个新的String个对象。
在第二个场景中,还会创建两个新对象,但会有一个StringStringBuffer。现在方式会很快吗?我有点困惑。

4 个答案:

答案 0 :(得分:5)

在你的两个场景之间,选项1每次都会更快,除非JITC做了我不期望的事情。不过,我确实不希望它有所作为,除非你非常频繁地调用这些方法。

为什么呢?因为您实际上没有使用选项1创建新对象。编译器应该执行常量折叠,将"Hello" + "World..!"转换为"HelloWorld..!"。因为这是一个编译时常量String,所以它会在VM启动时自动插入String池。因此,每次调用该方法时,您只需参考该标准" canonical" String。不执行任何对象创建。

在选项2中,您始终会创建多个对象 - StringBuffer(顺便说一下,您应使用StringBuilder),支持char[]和结果{{1 (至少)。并且在紧密循环中执行此操作效率不高。

此外,选项1更具可读性,在编写代码时应始终考虑这一点。

证明:

鉴于此测试代码:

String

使用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

这里是选项1的字节码:

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;

嗯,这很简短。如您所见,JVM从池中加载常量( public java.lang.String getTestMessageA(); Code: 0: ldc #2 // String HelloWorld..! 2: areturn )并返回它。没有直接在方法中创建对象。

现在这里是选项2的字节码:

ldc

因此,此代码创建一个新的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()

因此,选项2将在每次调用时创建两个新对象,并执行更多指令。因此,选项1会更快。

答案 1 :(得分:5)

"Hello" + "World..!"

是一个常量表达式。编译器将执行连接并将其作为单个String常量添加到字节代码中。因此,JVM只为它创建一个String对象。

return new StringBuffer("Hello").append("World..!").toString();

有两个String文字。 JVM将为每个创建一个对象。然后,它将创建一个StringBuffer对象和所有相应的后备char[],最后创建String方法的toString()对象返回值。

答案 2 :(得分:1)

我很想知道我遇到this article后面的场景,这可以证明我们完全错了。

文章说 +运算符看似无辜,但生成的字节码会产生一些惊喜。使用StringBuffer进行连接实际上可以生成比使用String要快得多的代码。

String连接生成的字节码创建一个StringBuffer对象,然后调用其append方法。在StringBuffer对象上执行连接后,它必须是转换回String。这是通过调用toString方法完成的。此方法从临时String对象创建新的StringBuffer对象。

总之,String conactenation inturn创建了三个对象:

一个String对象, 一个StringBuffer对象, 一个String对象。

但是,在第二种情况下无需创建临时StringBuffer

请参阅article link获取完整信息

答案 3 :(得分:0)

性能方面,StringBuffer在执行连接时速度更快。这是因为当你连接一个String时,你每次都在内部创建一个新对象,因为String是不可变的。

对于较小的字符串(附加一个或两个字符串)第一种方法没有问题,考虑一下这会降低性能。

来到这种情况:

如果您添加

     "value" + 5

Java必须首先将5转换为String。如果你看一下这个字节代码,Java实际上会调用String.valueOf(5)。查看该方法,您会发现Java创建了一个char数组和一个String(2个对象)来构建该值。

当Java追加5时,它只在StringBuilder上调用append(int)。该方法只是将值复制到内部字符缓冲区而不创建任何额外的对象。

内部追加

if (str == null) str = "null";
    int len = str.length();
if (len == 0) return this;
int newCount = count + len;
if (newCount > value.length)
    expandCapacity(newCount);
str.getChars(0, len, value, count);
count = newCount;
return this;

这使用本机方法System.arrayCopy(),这里是http://www.cafeaulait.org/course/week2/50.html

请参阅此What is the difference between String and StringBuffer in Java?