Java 基礎(chǔ)面試真題:String 為什么是不可變的?
今天來分享一道群友去阿里云面試遇到的 Java 基礎(chǔ)面試真題:“String
、StringBuffer
、StringBuilder
的區(qū)別?String
為什么是不可變的?”。
網(wǎng)站很多文章都把 String
不可變的原因講錯了,建議你重點關(guān)注一下。另外,本文還提到了 :“Java 9 為何要將 String 的底層實現(xiàn)由 char[]
改成了 byte[]
?”
下面是正文。
可變性
簡單的來說:String
類中使用 final
關(guān)鍵字修飾字符數(shù)組來保存字符串,所以String
對象是不可變的。×
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { private final char value[]; //... }
?? 修正 :我們知道被
final
關(guān)鍵字修飾的類不能被繼承,修飾的方法不能被重寫,修飾的變量是基本數(shù)據(jù)類型則值不能改變,修飾的變量是引用類型則不能再指向其他對象。因此,final
關(guān)鍵字修飾的數(shù)組保存字符串并不是String
不可變的根本原因,因為這個數(shù)組保存的字符串是可變的(final
修飾引用類型變量的情況)。
String
真正不可變有下面幾點原因:保存字符串的數(shù)組被
final
修飾且為私有的,并且String
類沒有提供/暴露修改這個字符串的方法。
String
類被final
修飾導(dǎo)致其不能被繼承,進(jìn)而避免了子類破壞String
不可變。相關(guān)閱讀:如何理解 String 類型值的不可變?- 知乎提問[1]
補(bǔ)充(來自issue 675[2]):在 Java 9 之后,
String
、StringBuilder
與StringBuffer
的實現(xiàn)改用byte
數(shù)組存儲字符串。Java 9 為何要將
String
的底層實現(xiàn)由char[]
改成了byte[]
?新版的 String 其實支持兩個編碼方案:Latin-1 和 UTF-16。如果字符串中包含的漢字沒有超過 Latin-1 可表示范圍內(nèi)的字符,那就會使用 Latin-1 作為編碼方案。Latin-1 編碼方案下,
byte
占一個字節(jié)(8 位),char
占用 2 個字節(jié)(16),byte
相較char
節(jié)省一半的內(nèi)存空間。如果字符串中包含的漢字超過 Latin-1 可表示范圍內(nèi)的字符,
byte
和char
所占用的空間是一樣的。這是官方的介紹:https://openjdk.java.net/jeps/254 。
StringBuilder
與 StringBuffer
都繼承自 AbstractStringBuilder
類,在 AbstractStringBuilder
中也是使用字符數(shù)組保存字符串,不過沒有使用 final
和 private
關(guān)鍵字修飾,最關(guān)鍵的是這個 AbstractStringBuilder
類還提供了很多修改字符串的方法比如 append
方法。
abstract class AbstractStringBuilder implements Appendable, CharSequence { char[] value; public AbstractStringBuilder append(String str) { if (str == null) return appendNull(); int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this; } //... }
線程安全性
String
中的對象是不可變的,也就可以理解為常量,線程安全。AbstractStringBuilder
是 StringBuilder
與 StringBuffer
的公共父類,定義了一些字符串的基本操作,如 expandCapacity
、append
、insert
、indexOf
等公共方法。StringBuffer
對方法加了同步鎖或者對調(diào)用的方法加了同步鎖,所以是線程安全的。StringBuilder
并沒有對方法進(jìn)行加同步鎖,所以是非線程安全的。
性能
每次對 String
類型進(jìn)行改變的時候,都會生成一個新的 String
對象,然后將指針指向新的 String
對象。StringBuffer
每次都會對 StringBuffer
對象本身進(jìn)行操作,而不是生成新的對象并改變對象引用。相同情況下使用 StringBuilder
相比使用 StringBuffer
僅能獲得 10%~15% 左右的性能提升,但卻要冒多線程不安全的風(fēng)險。
對于三者使用的總結(jié):
- 操作少量的數(shù)據(jù): 適用
String
- 單線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用
StringBuilder
- 多線程操作字符串緩沖區(qū)下操作大量數(shù)據(jù): 適用
StringBuffer
到此這篇關(guān)于Java 基礎(chǔ)面試真題:String 為什么是不可變的?的文章就介紹到這了,更多相關(guān)String 為什么是不可變的?內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
3行代碼快速實現(xiàn)Spring Boot Oauth2服務(wù)功能
oauthserver是一個基于Spring Boot Oauth2的完整的獨立的Oauth服務(wù)器。僅僅需要創(chuàng)建相關(guān)數(shù)據(jù)表,修改數(shù)據(jù)庫的連接信息,你就可以得到一個Oauth服務(wù)器。這篇文章給大家介紹3行代碼快速實現(xiàn)Spring Boot Oauth2服務(wù)功能,需要的朋友參考下吧2018-04-04JAVA初級項目——實現(xiàn)圖書管理系統(tǒng)
這篇文章主要介紹了JAVA如何實現(xiàn)圖書管理系統(tǒng),文中示例代碼非常詳細(xì),供大家參考和學(xué)習(xí),感興趣的朋友可以了解下2020-06-06詳解JAVA高質(zhì)量代碼之?dāng)?shù)組與集合
在學(xué)習(xí)編程的過程中,我覺得不止要獲得課本的知識,更多的是通過學(xué)習(xí)技術(shù)知識提高解決問題的能力,這樣我們才能走在最前方,本文主要講述Java高質(zhì)量代碼之?dāng)?shù)組與集合2013-08-08使用SpringBoot根據(jù)配置注入接口的不同實現(xiàn)類(代碼演示)
使用springboot開發(fā)時經(jīng)常用到@Autowired和@Resource進(jìn)行依賴注入,但是當(dāng)我們一個接口對應(yīng)多個不同的實現(xiàn)類的時候如果不進(jìn)行一下配置項目啟動時就會報錯,那么怎么根據(jù)不同的需求注入不同的類型呢,感興趣的朋友一起看看吧2022-06-06圖解Java經(jīng)典算法冒泡排序的原理與實現(xiàn)
冒泡排序是一種簡單的排序算法,它也是一種穩(wěn)定排序算法。其實現(xiàn)原理是重復(fù)掃描待排序序列,并比較每一對相鄰的元素,當(dāng)該對元素順序不正確時進(jìn)行交換。一直重復(fù)這個過程,直到?jīng)]有任何兩個相鄰元素可以交換,就表明完成了排序2022-09-09