1. String的基本特性
- String:字符串,使用一對""引號來表示。
String s1 = "hello"; //字面量的定義方式
String s2 = new String("hello");
- String聲明為final的,不可被繼承
- String實現了
Serializable
接口,表示字符串是支持序列化的;實現了Comparable
,表示String可以比較大小。 - String在JDK8及以前內部定義了
final char[] value
用于存儲字符串數據。JDK9時改為byte[]
。 - String:代表不可變的字符序列。簡稱不可變性。
- 當對字符串重新賦值時,需要重寫指定內存區域賦值,不能使用原有的value進行賦值。
- 當對現有的字符串進行連接操作時,也需要重新指定內存區域賦值,不能使用原有的value進行賦值。
- 當調用String的
replace()
方法修改指定字符或字符串時,也需要重新指定內存區域賦值,不能使用原有的value進行賦值。
- 通過字面量的方式(區別于new)給一個字符串賦值,此時的字符串值聲明在字符串常量池中
-
字符串常量池中是不會存儲相同內容的字符串的。
- String的String Pool是一個固定大小的Hashtable,默認值大小長度是1009。如果放進String Pool的String非常多,就會造成Hash沖突嚴重,從而導致鏈表會很長,而鏈表長了后直接會造成的影響就是當調用
String.intern
時性能會大幅下降。 - 使用
-XX:StringTableSize
可設置StringTable的長度 - 在JDK6中StringTable是固定的,就是1009的長度,所以如果常量池中的字符串過多就會導致效率下降很快。StringTableSize設置沒有要求
- 在JDK7中,StringTable的長度默認值是60013。
- JDK8開始,設置StringTable的長度的話,1009是可設置的最小值。
- String的String Pool是一個固定大小的Hashtable,默認值大小長度是1009。如果放進String Pool的String非常多,就會造成Hash沖突嚴重,從而導致鏈表會很長,而鏈表長了后直接會造成的影響就是當調用
2. String的內存分配
- 在Java語言中有8種基本數據類型和一種比較特殊的類型String。這些類型為了使它們在運行過程中速度更快、更節省內存,都提供了一種常量池的概念。
- 常量池就類似于一個Java系統級別的緩存。8種基本數據類型的常量池都是系統協調的,String類型的常量池比較特殊,它的主要使用方法有兩種:
- 直接使用雙引號聲明出來的String對象會直接存儲在常量池中。比如
String info = "hello";
- 如果不是雙引號聲明的String對象,可以使用String提供的intern()方法。
- 直接使用雙引號聲明出來的String對象會直接存儲在常量池中。比如
- Java6及以前,字符串常量池存放在永久代。
- Java7中Oracle的工程師對字符串的邏輯做了很大的改變,即將字符串常量池的位置調整到Java堆內。
- 所有的字符串都保存在堆(Heap)中,和其它普通對象一樣,這樣可以讓你在進行調優應用時僅需要調整堆大小就可以了。
- 字符串常量池概念原本使用得比較多,但是這個改動使得我們有足夠得理由讓我們重新考慮在Java7中使用String.intern()。
- Java8永久代變為元空間,字符串常量仍在堆中。
3. String的基本操作
Java語言規范里要求完全相同的字符串字面量,應該包含同樣的Unicode字符序列(包含同一份碼點序列的常量),并且必須是指向同一個String類實例。
4. 字符串拼接操作
- 常量與常量的拼接結果在常量池,原理是編譯期優化。
- 常量池中不會存在相同內容的常量。
- 只要其中有一個是變量(
s1 = s2 + "hello";//s2不能用final修飾
),結果就在堆中。變量拼接的原理是StringBuilder。 - 如果拼接的結果調用intern()方法,則主動將常量池中還沒有的字符串對象放入池中,并返回此對象地址。
intern():首先判斷字符串常量池中是否存在對應的字符串值,如果存在,則返回常量池中該字符串的地址;如果不存在,則在常量池中加載該字符串,并返回該字符串的地址。
5. intern()的使用
如果不是使用雙引號聲明的String對象,可以使用String提供的intern方法:intern方法會從字符串常量池中查詢當前字符串是否存在,若不存在就會將當前字符串放入常量池中。
表達式("a" + "b" + "c").intern() == "abc"
的值必定是true。
intern()
方法可以確保字符串在內存里只有一份拷貝,這樣可以節約內存空間,加快字符串操作任務的執行速度。注意,這個值會被存放在字符串內部池(String Intern Pool)。
JDK6 vs JDK7/8
- JDK1.6中,將這個字符串對象嘗試放入串池。
- 如果串池中有,則并不會放入。返回已有的串池中的對象的地址。
- 如果沒有,會把此對象復制一份,放入串池,并返回串池中的對象地址。
- JDK1.7起,將這個字符串對象嘗試放入串池。
- 如果串池中有,則并不會放入。返回已有的串池中的對象的地址。
- 如果沒有,會把對象的引用地址復制一份,放入串池,并返回串池中的對象地址。
當程序中存在大量重復的字符串時,使用intern()
可以節省內存空間。