将创build多less个String对象

我有以下Java代码:

public String makinStrings() { String s = "Fred"; s = s + "47"; s = s.substring(2, 5); s = s.toUpperCase(); return s.toString(); } 

这个问题很简单:调用这个方法时会创build多less个String对象?

在开始的时候,我回答说创build了5个String对象,但是我的书的答案是只有3个对象被创build,没有给出解释(这是一个SCJP问题)。

从我的angular度来看,有5个对象:“弗雷德”,“47”,“弗雷德47”,“ED4”,“ED4”。

我也在一个SCJP模拟考试中发现了这个问题,同样的答案3。

“Fred”和“47”将来自string文字池。 因此,当方法被调用时,它们不会被创build。 相反,当类被加载时(或者更早,如果其他类使用具有相同值的常量),它们将被放置在那里。

“Fred47”,“ed4”和“ED4”是将在每个方法调用中创build的3个String对象。

程序的代码中往往含有大量的string文字。 在Java中,这些常量被收集在一个叫做string表的地方以提高效率。 例如,如果在十个不同的地方使用string"Name: " ,则JVM(通常)只有该String的一个实例,在所有使用它的十个地方,引用都指向一个实例。 这节省了内存。

这个优化是可能的,因为String不可变的 。 如果有可能改变一个string,改变它一个地方将意味着其他九个改变。 这就是为什么任何改变string的操作都会返回一个新的实例。 这就是为什么如果你这样做:

 String s = "boink"; s.toUpperCase(); System.out.println(s); 

它打印boink ,而不是BOINK

现在还有一个棘手的问题: java.lang.String多个实例可能指向相同的底层char[]作为它们的字符数据,换句话说,它们可能是同一个char[]上的不同视图 ,只需使用一个slice的数组。 再次,对效率进行优化。 substring()方法是发生这种情况的一种情况。

 s1 = "Fred47"; //String s1: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=0, length=6 // ^........................^ s2 = s1.substring(2, 5); //String s2: data=[ 'F', 'r', 'e', 'd', '4', '7'], offset=2, length=3 // ^.........^ // the two strings are sharing the same char[]! 

在你的SCJP问题中,所有这些归结为:

  • string"Fred"取自String表。
  • string"47"取自String表。
  • string"Fred47"在方法调用期间创build。 // 1
  • 在方法调用期间创buildstring"ed4""Fred47" // 2 共享相同的后备数组
  • string"ED4"在方法调用期间创build。 // 3
  • s.toString()不创build一个新的,它只是返回this

一个有趣的边缘情况:考虑如果你有一个非常长的string,例如,从互联网上获取的网页,假设char[]的长度是两兆字节会发生什么情况。 如果你把这个substring(0, 4) ,你得到一个新的string, 看起来只有四个字符长,但它仍然共享这两兆字节的后备数据。 这在现实世界中并不常见,但这可能是一个巨大的内存浪费! 在这种罕见的情况下,如果遇到这个问题,可以使用new String(hugeString.substring(0, 4))来创build一个带有新的小的后备数组的String。

最后,可以在运行时通过调用intern()将string强制到string表中。 在这种情况下的基本规则:不要这样做。 扩展规则:除非您使用内存分析器来确定这是一个有用的优化,否则不要这样做。

基于javap输出,看起来像concatenation期间创build了一个StringBuilder,而不是一个String。 然后有三个string调用substring(),toUpperCase()和toString()。

最后一次调用不是多余的,因为它将StringBuilder转换为String。

 >javap -c Test Compiled from "Test.java" public java.lang.String makinStrings(); Code: 0: ldc #5; //String Fred 2: astore_1 3: new #6; //class java/lang/StringBuilder 6: dup 7: invokespecial #7; //Method java/lang/StringBuilder."<init>":()V 10: aload_1 11: invokevirtual #8; //Method java/lang/StringBuilder.append: (Ljava/lang/String;)Ljava/lang/StringBuilder; 14: ldc #9; //String 47 16: invokevirtual #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: invokevirtual #10; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 22: astore_1 23: aload_1 24: iconst_2 25: iconst_5 26: invokevirtual #11; //Method java/lang/String.substring:(II)Ljava/lang/String; 29: astore_1 30: aload_1 31: invokevirtual #12; //Method java/lang/String.toUpperCase:()Ljava/lang/String; 34: astore_1 35: aload_1 36: invokevirtual #13; //Method java/lang/String.toString:()Ljava/lang/String; 39: areturn 

}