java - Java中StringBuffer和String的使用场景

标签 java performance core

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

1.

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

2.

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

在第一种情况下,将创建两个新的 String 对象。
在第二种情况下,也会创建两个新对象,但会有一个 StringStringBuffer。现在他们的方式会很快吗?我有点困惑。

最佳答案

在您的两个场景之间,选项 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/

相关文章:

java - 如何将 JSON 对象发布到 URL?

java - Hibernate 错误 : org. hibernate.NonUniqueObjectException:具有相同标识符值的不同对象已与 session 关联

c++ - 浮点异常、段错误和类似错误

c - sparc64 上的 DBX 核心转储和堆栈跟踪输入/输出寄存器

java - 指定 Maven 分类器的依赖关系

java - 如何使用 java.security.Signature 进行验证

javascript - 为什么在文档元素上触发布局?

python - 使用 cython 进行慢速多处理

performance - 进度报告

c++ - 获取“段错误 : core dumped when trying to write string to setter