我有以下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。
答案 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
}