繁体   English   中英

字符串对象。 需要澄清

[英]String Object. Clarification needed

假设我的程序中有以下行:

jobSetupErrors.append("abc");

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

  1. 创建新的String对象并为其指定值“abc”
  2. 该String对象的值被赋给现有的StringBuilder对象

如果这是正确的,我再添加1行......

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

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

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

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

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

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

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

在第二个示例中,您只是创建了对相同String的额外引用,但这是Java已经为您完成的操作,方法是将其引用到名为字符串池的内容中。 这是第一次看到“abc”时发生的; 第二次,它检查池并发现“abc”已经存在,因此它被替换为与第一个相同的引用。

有关String interning的更多信息,请参见http://en.wikipedia.org/wiki/String_interning

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

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是一个非常酷的工具,当你想知道到底是怎么回事)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM