方案往往含有大量的字符串文字在他們的代碼。在Java中,爲了提高效率,這些常量被收集在一個稱爲字符串表的東西中。例如,如果在十個不同的地方使用字符串"Name: "
,則JVM(通常)只有該String的一個實例,並且在使用該字符串的所有十個位置中,引用都指向該實例。這節省了內存。
這種優化是可能的,因爲String
是不可變的。如果可以更改字符串,將其更改爲一個地方意味着它會改變其他九個字符串。這就是爲什麼任何改變String的操作都會返回一個新的實例。這就是爲什麼如果你這樣做:
String s = "boink";
s.toUpperCase();
System.out.println(s);
它打印boink
,不BOINK
。
現在有一個更靠譜一點:在相同的基礎char[]
他們的字符數據java.lang.String
可能點的多個實例,換句話說,也可以是不同的同char[]
意見,只用一個片的陣列。再次,對效率進行優化。 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"
從字符串表中獲得。
- 字符串
"47"
從字符串表中獲得。
- 字符串
"Fred47"
在方法調用期間創建的。// 1
- 字符串
"ed4"
在方法調用期間創建,共享相同的背襯陣列"Fred47"
// 2
- 方法調用過程中創建
"ED4"
的字符串。 // 3
s.toString()
不會創建一個新的,它只會返回this
。
之一的這一切有趣的邊緣情況:考慮,如果你有一個很長的字符串會發生什麼,例如,從網上取一個網頁,讓我們假設char[]
的長度爲2兆字節。如果你拿這substring(0, 4)
,你會得到一個新的字符串,看起來像它只有四個字符長,但它仍然共享這兩兆字節的支持數據。這在現實世界中並不常見,但它可能會造成巨大的內存浪費!在(罕見)情況下,如果遇到此問題,可以使用new String(hugeString.substring(0, 4))
創建一個帶有新的小型後備陣列的字符串。
最後,可以在運行時通過調用intern()
來強制字符串進入字符串表。這種情況下的基本規則:不要這樣做。擴展規則:除非您使用內存分析器來確定它是一種有用的優化,否則不要這樣做。
我想象中的編譯器將內聯前兩個語句和刪除最後一個(冗餘)方法調用。這給你留下了「Fred47」,「ed4」,「ED4」。 –
@Jared's'不是*編譯時常量表達式*,因此不會發生。 (編譯代碼並使用'javap -c',甚至'strings'。) –
可能重複的[Java - 多少個字符串對象?](http://stackoverflow.com/questions/17898236/java-how-many -string-objects) –