说我的程序中有以下行:
jobSetupErrors.append("abc");
在上面的例子中,jobSetupErrors是一个StringBuilder(),我看到的是:
如果这是正确的,我再添加一行......
jobSetupErrors.append("abc");
logger.info("abc");
在上面的示例中,我们分别创建了String对象 2次?
如果是这样,做这样的事情会更合适吗?
String a = "abc";
jobSetupErrors.append(a);
logger.info(a);
这是一种更好的方法吗?请指教
答案 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是一个非常酷的工具,当你想知道到底发生了什么)