一. String問題答疑
- String字符串是如何設計與實現考量的?
- String字符串緩存 intern()方法,由永久代移到堆中。
- String 的演化,Java 9 中底層把 char 數組換成了 byte 數組,佔用更少的空間
二. String的創建機理
由於String在Java世界中使用過於頻繁,Java爲了避免在一個系統中產生大量的String對象,引入了字符串常量池。其運行機制是:創建一個字符串時,首先檢查池中是否有值相同的字符串對象,如果有則不需要創建直接從池中剛查找到的對象引用;如果沒有則新建字符串對象,返回對象引用,並且將新創建的對象放入池中。但是,通過new方法創建的String對象是不檢查字符串池的,而是直接在堆區或棧區創建一個新的對象,也不會把對象放入池中。上述原則只適用於通過直接量給String對象引用賦值的情況。博客
舉例:String str1 = "123"; //通過直接量賦值方式,放入字符串常量池
String str2 = new String(“123”);//通過new方式賦值方式,不放入字符串常量池
注意:String提供了inter()方法。調用該方法時,如果常量池中包括了一個等於此String對象的字符串(由equals方法確定),則返回池中的字符串。否則,將此String對象添加到池中,並且返回此池中對象的引用。
三. StringBuffer/StringBuilder
StringBuffer和StringBuilder都實現了AbstractStringBuilder抽象類,擁有幾乎一致對外提供的調用接口;其底層在內存中的存儲方式與String相同,都是以一個有序的字符序列(char類型的數組)進行存儲,不同點是StringBuffer/StringBuilder對象的值是可以改變的,並且值改變以後,對象引用不會發生改變;兩者對象在構造過程中,首先按照默認大小申請一個字符數組,由於會不斷加入新數據,當超過默認大小後,會創建一個更大的數組,並將原先的數組內容複製過來,再丟棄舊的數組。因此,對於較大對象的擴容會涉及大量的內存複製操作,如果能夠預先評估大小,可提升性能。
四. String類的考點分析
- 通過 String 和相關類,考察基本的線程安全設計與實現,各種基礎編程實踐。
- 考察 JVM 對象緩存機制的理解以及如何良好地使用。
- 考察 JVM 優化 Java 代碼的一些技巧。
- String 相關類的演進,比如 Java 9 中實現的巨大...
五. String技術點深入分析
5.1 String類是典型的Immutable類
是典型的 Immutable 類,被聲明成爲 final class,所有屬性也都是 final 的。也由於它的不可變,類似拼接、裁剪字符串等動作,都會產生新的 String 對象。
5.2 字符串設計和實現考量
- String 是 Immutable 類的典型實現,原生的保證了基礎線程安全,因爲你無法對它內部數據進行任何修改,這種便利甚至體現在拷貝構造函數中,由於不可變,Immutable 對象在拷貝時不需要額外複製數據。
- 爲了實現修改字符序列的目的,StringBuffer 和 StringBuilder 底層都是利用可修改的(char,JDK 9 以後是 byte)數組,二者都繼承了 AbstractStringBuilder,裏面包含了基本操作,區別僅在於最終的方法是否加了 synchronized。
- 這個內部數組應該創建成多大的呢?如果太小,拼接的時候可能要重新創建足夠大的數組;如果太大,又會浪費空間。目前的實現是,構建時初始字符串長度加 16(這意味着,如果沒有構建對象時輸入最初的字符串,那麼初始值就是 16)。我們如果確定拼接會發生非常多次,而且大概是可預計的,那麼就可以指定合適的大小,避免很多次擴容的開銷。擴容會產生多重開銷,因爲要拋棄原有數組,創建新的(可以簡單認爲是倍數)數組,還要進行arraycopy。
5.3 字符串緩存
- String 在 Java 6 以後提供了 intern()方法,目的是提示 JVM 把相應字符串緩存起來,以備重複使用。在我們創建字符串對象並調用 intern() 方法的時候,如果已經有緩存的字符串,就會返回緩存裏的實例,否則將其緩存起來。
- 在後續版本中,這個緩存被放置在堆中,這樣就極大避免了永久代佔滿的問題,甚至永久代在 JDK 8 中被 MetaSpace(元數據區)替代了。而且,默認緩存大小也在不斷地擴大中,從最初的 1009,到 7u40 以後被修改爲 60013。
六. String不可變的好處
6.1 可以緩存 hash 值 博客
因爲 String 的 hash 值經常被使用,例如 String 用做 HashMap 的 key。不可變的特性可以使得 hash 值也不可變,因此只需要進行一次計算。
6.2 String Pool 的需要
如果一個String對象已經被創建過了,那麼就會從 String Pool 中取得引用。只有 String 是不可變的,纔可能使用 String Pool。
6.3 安全性
String 經常作爲參數,String 不可變性可以保證參數不可變。例如在作爲網絡連接參數的情況下如果 String 是可變的,那麼在網絡連接過程中,String 被改變,改變 String 對象的那一方以爲現在連接的是其它主機,而實際情況卻不一定是。
6.4 線程安全
String 不可變性天生具備線程安全,可以在多個線程中安全地使用。