字符串对象。需要澄清

时间:2010-06-15 12:59:18

标签: java string stringbuilder

说我的程序中有以下行:

jobSetupErrors.append("abc");

在上面的例子中,jobSetupErrors是一个StringBuilder(),我看到的是:

  1. 创建新字符串对象并为其指定值“abc”
  2. 该String对象的值被分配给现有的StringBuilder对象
  3. 如果这是正确的,我再添加一行......

    jobSetupErrors.append("abc");
    logger.info("abc");
    

    在上面的示例中,我们分别创建了String对象 2次?

    如果是这样,做这样的事情会更合适吗?

    String a = "abc";
    jobSetupErrors.append(a);
    logger.info(a);
    

    这是一种更好的方法吗?请指教

2 个答案:

答案 0 :(得分:8)

  

在上面的例子中我们正在创建   字符串对象分别2次?

不,因为在Java中,字符串文字(双引号中的任何内容)都是实体。这意味着这两行都指向相同的String,因此不需要进一步优化。

在你的第二个例子中,你只是创建了对同一个String的额外引用,但这就是Java已经为你做的事情,通过在一个叫做字符串池的东西中放置它。这是第一次看到“abc”时发生的;第二次,它检查池并发现“abc”已经存在,因此它被替换为与第一个相同的引用。

有关字符串实习的更多信息,请参阅http://en.wikipedia.org/wiki/String_interning

答案 1 :(得分:4)

为了帮助找到答案,我写了一个类似下面的类:

class Test {
  String a = "abc" ;
  StringBuilder buffer = new StringBuilder() ;

  public void normal() {
    buffer.append( "abc" ) ;
    buffer.append( "abc" ) ;
  }

  public void clever() {
    buffer.append( a ) ;
    buffer.append( a ) ;
  }
}

如果我们编译它,然后在它上面运行javap来提取字节码:

14:09:58 :: javap $ javap -c Test
Compiled from "Test.java"
class Test extends java.lang.Object{
java.lang.String a;

java.lang.StringBuilder buffer;

Test();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
   4:   aload_0
   5:   ldc #2; //String abc
   7:   putfield    #3; //Field a:Ljava/lang/String;
   10:  aload_0
   11:  new #4; //class java/lang/StringBuilder
   14:  dup
   15:  invokespecial   #5; //Method java/lang/StringBuilder."<init>":()V
   18:  putfield    #6; //Field buffer:Ljava/lang/StringBuilder;
   21:  return

public void normal();
  Code:
   0:   aload_0
   1:   getfield    #6; //Field buffer:Ljava/lang/StringBuilder;
   4:   ldc #2; //String abc
   6:   invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   9:   pop
   10:  aload_0
   11:  getfield    #6; //Field buffer:Ljava/lang/StringBuilder;
   14:  ldc #2; //String abc
   16:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   19:  pop
   20:  return

public void clever();
  Code:
   0:   aload_0
   1:   getfield    #6; //Field buffer:Ljava/lang/StringBuilder;
   4:   aload_0
   5:   getfield    #3; //Field a:Ljava/lang/String;
   8:   invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  pop
   12:  aload_0
   13:  getfield    #6; //Field buffer:Ljava/lang/StringBuilder;
   16:  aload_0
   17:  getfield    #3; //Field a:Ljava/lang/String;
   20:  invokevirtual   #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   23:  pop
   24:  return

}

我们可以看到两件事。

首先,normal方法对这两个调用使用相同的String实例(实际上它与在初始化块中设置为此类的a成员变量的文字相同)

其次,clever方法比normal方法长。这是因为从类中获取属性需要额外的步骤。

故事的寓意是99%的时间,Java在它自己的事情上做正确的事情,并且没有必要试图变得聪明;-)(和javap是一个非常酷的工具,当你想知道到底发生了什么)