《Java架構築基》從Java基礎講起——String類深入理解

一. 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 不可變性天生具備線程安全,可以在多個線程中安全地使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章