将创建多少个String对象

时间:2011-09-10 08:36:19

标签: java scjp

我有以下Java代码:

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

问题有点简单:调用此方法时将创建多少String个对象?

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

从我的观点来看,有5个对象: “Fred”,“47”,“Fred47”,“ed4”,“ED4”。

我也在SCJP模拟考试中找到了这个问题,答案相同3。

3 个答案:

答案 0 :(得分:15)

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

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

答案 1 :(得分:2)

程序往往在代码中包含大量的字符串文字。在Java中,为了提高效率,这些常量被收集在称为字符串表的内容中。例如,如果在十个不同的位置使用字符串"Name: ",则JVM(通常)只有该String的一个实例,并且在使用它的所有十个位置中,引用都指向该一个实例。这节省了内存。

此优化是可行的,因为String 不可变。如果可以更改字符串,将其更改为一个位置将意味着它在其他九个中也会发生变化。这就是为什么任何更改String的操作都会返回一个新实例。这就是为什么如果你这样做:

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

它打印boink,而不是BOINK

现在还有一个棘手的问题:java.lang.String 的多个实例可能指向其角色数据的相同底层char[],换句话说,它们可能不同<通过仅使用数组的一部分,在同一char[]上的em> views 。再次,优化效率。 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问题中,所有这些归结为:

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

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

最后,可以通过在其上调用intern()来在运行时强制将String强制转换为字符串表。在这种情况下的基本规则:不要这样做。扩展规则:除非您使用内存分析器确定它是有用的优化,否则不要这样做。

答案 2 :(得分:2)

基于javap输出,看起来在连接期间创建了StringBuilder,而不是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

}