為什麼Java字符串是不可變對象?(2)

2020-11-23 51CTO

答案二:

這是一個老生常談的話題(This is an old yet still popular question). 在Java中將String設計成不可變的是綜合考慮到各種因素的結果,想要理解這個問題,需要綜合內存,同步,數據結構以及安全等方面的考慮. 在下文中,我將為各種原因做一個小結。

1. 字符串常量池的需要

字符串常量池(String pool, String intern pool, String保留池) 是Java堆內存中一個特殊的存儲區域, 當創建一個String對象時,假如此字符串值已經存在於常量池中,則不會創建一個新的對象,而是引用已經存在的對象。

如下面的代碼所示,將會在堆內存中只創建一個實際String對象.

  1. String s1 = "abcd"
  2. String s2 = "abcd"

示意圖如下所示:

請思考: 假若代碼如下所示,s1和s2還會指向同一個實際的String對象嗎?假若字符串對象允許改變,那麼將會導致各種邏輯錯誤,比如改變一個對象會影響到另一個獨立對象. 嚴格來說,這種常量池的思想,是一種優化手段.

  1. String s1= "ab" + "cd"
  2. String s2= "abc" + "d"

也許這個問題違反新手的直覺, 但是考慮到現代編譯器會進行常規的優化, 所以他們都會指向常量池中的同一個對象. 或者,你可以用 jd-gui 之類的工具查看一下編譯後的class文件.

2. 允許String對象緩存HashCode

Java中String對象的哈希碼被頻繁地使用, 比如在hashMap 等容器中。

字符串不變性保證了hash碼的唯一性,因此可以放心地進行緩存.這也是一種性能優化手段,意味著不必每次都去計算新的哈希碼. 在String類的定義中有如下代碼:

private int hash;//用來緩存HashCode

3. 安全性

String被許多的Java類(庫)用來當做參數,例如 網絡連接地址URL,文件路徑path,還有反射機制所需要的String參數等, 假若String不是固定不變的,將會引起各種安全隱患。

假如有如下的代碼:

  1. boolean connect(string s){ 
  2.  
  3. if (!isSecure(s)) { 
  4.  
  5. throw new SecurityException(); 
  6.  
  7.  
  8.  
  9.  
  10. causeProblem(s); 
  11.  

總體來說, String不可變的原因包括 設計考慮,效率優化問題,以及安全性這三大方面. 事實上,這也是Java面試中的許多 「為什麼」 的答案。

點讚 0

相關焦點

  • 為什麼Java字符串是不可變對象?(3)
    為什麼Java字符串是不可變對象?(3) 本文主要來介紹一下Java中的不可變對象,以及Java中String類的不可變性,那麼為什麼Java的String類是不可變對象?讓我們一起來分析一下。
  • 萬字梳理,帶你拿下 Java 面試題!
    使用 new 關鍵字會在堆中創建一個對象,另外一個對象是 abc ,它會在常量池中創建,所以一共創建了兩個對象;如果 abc 在常量池中已經存在的話,那麼就會創建一個對象。8、String 為什麼是不可變的、jdk 源碼中的 String 如何定義的、為什麼這麼設計?
  • Java第七講:字符串
    字符串是由一對雙引號括起來的任意長度的字符序列。例如「你好」、「%&」、「 」,但是注意字符串不能包含斷行哦。字符串不是一個基本的類型(類似int和double),而是一個類。我們要創建一個字符串類的方法是:String greeting = 「你好」,它是String greeting = new String("你好")的縮寫形式。字符串在這方面是唯一的,字符串類的對象是唯一一種不需要new就可以創建的。我們可以用「+」號來連接字符串。
  • 「JAVA」萬字長篇詳述字節碼對象與反射機制完成動態編程
    2.類的連結當類的字節碼文件被加載進JVM內存之後,JVM便會創建一個對應的Class對象(也可以叫字節碼對象),把字節碼指令中對常量池中的索引引用轉換為直接引用,接著把類的字節碼指令合併到JRE中。使用類字面量,即使用類的class屬性;Class<java.util.Date> clazz1 = java.util.Date.class;2.使用對象的getClass();方法;java.util.Date
  • JSON對象和簡單練習
    commons-lang-xx.jarcommons-collections-3.2.2.jarezmorph-1.0.6.jarjson-lib-2.4-jdk15.jar2把字符串改成JSON>上一篇我們在SearchBookAJAXServlet.java中是用字符串拼接,這次我們這裡改成JSON。
  • Java字符串地查找操作
    調用語法如下:str.indexOf(s)其中,str是已創建的字符串對象,s待查找的字符串。案例1:創建一個字符串對象,使用indexOf()方法查找子串,如查找到子串輸出子串內容,否則輸出查找子串失敗信息。在PbaseUnit11項目unit包下創建StringSearchSample類。
  • Springmvc框架對json的支持 Java程式設計師必看
    但是在寫json的時候還是出現了許多問題,用eclipse寫js代碼經常除了bug卻不知道問題出來哪裡。我們可以使用alert()或console.info()函數來進行判斷問題出在哪一行上。解決了發送json的問題後,又遇到一個bug,發送的json字符串返回的確實一個http415錯誤代碼,大概是說後端接收的參數類型不正確的意思,下面請看java代碼。
  • 反射——Java高級開發必須懂得
    一、Class類的使用任何一個類都是Class的實例對象,這個實例對象有三種表示方式1、任何一個類都有一個隱含的靜態成員變量classClass c1=Foo.class;2、已經知道該類的對象通過getClass
  • Java之File類的構造方法
    小編先來介紹一下構造方法1:File(String pathname)通過給定路徑名字符串轉換為抽象路徑名來創建一個新File實例參數:String pathname:字符串的路徑名稱路徑可以是以文件結尾
  • Java8 lambda表達式語法
    但是有一點這裡強調一下(Windows系統):目前我們工作的版本一般是java 6或者java 7,所以很多人安裝java8基本都是學習為主。這樣就在自己的機器上會存在多版本的JDK。而且大家一般是希望在命令行中執行java命令是基於老版本的jdk。但是在安裝完jdk8並且沒有設置path的情況下,你如果在命令行中輸入:java -version,屏幕上會顯示是jdk 8。
  • java之字符緩衝輸出流,BufferedWriter的簡單介紹
    各位小夥伴們,大家好在之前的文章中小編介紹了字節緩衝流的相關知識,這次小編要介紹的是字符緩衝輸出流,BufferedWriter,具體如下:java.io.BufferedWriter extends Writer
  • Java反射機制深入詳解
    反射是從1.2就有的,後面的三大框架都會用到反射機制,涉及到類」Class」,無法直接new CLass(),其對象是內存裡的一份字節碼.Class 類的實例表示正在運行的 Java 應用程式中的類和接口。枚舉是一種類,注釋是一種接口。每個數組屬於被映射為 Class 對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。
  • 給JAVA程式設計師的正則表達式一課
    [abc]表示文本或字符串應為a或b或c。在任何表達式中添加「^」會否定該表達式的含義,例如[^abc]表示匹配任何除abc外的任何字符。()表示分組,分組後可以在後續反向引用。反向引用存儲與組匹配的字符串部分。可以使用符號$來引用特定的組。$1,$2…代表第一組1,第2組等。默認組為$0,表示字符串本身。例如,我們要刪除行中的所有空格。
  • 2020年Java基礎高頻面試題匯總
    一旦java看到null,就知道這個引用還沒有指向某個對象,2.基本數據類型在聲明時系統會自動給它分配空間,而引用類型聲明時只是分配了引用空間,必須通過實例化開闢數據空間之後才可以賦值。數組對象也是一個引用對象,將一個數組賦值給另一個數組時只是複製了一個引用,所以通過某一個數組所做的修改在另一個數組中也看的見。
  • JavaScript基礎-內置對象
    Date 對象和 Math 對象不一樣,Date是一個構造函數,所以使用時需要實例化後才能使用其中具體方法和屬性。銷毀臨時變量temp = null;字符串的不可變 指的是裡面的值不可變,雖然看上去可以改變內容,但其實是地址變了,內存中新開闢了一個內存空間。
  • Java中可變參數的使用方式
    在Java的參數定義中,有一種比較特殊的定義方式,就是可變參數。可能有的朋友對這個名稱有點陌生,但是看到代碼可能就會說,「就是他啊」。那這個可變參數是什麼呢,就是類似下面這個方法籤名中的參數的定義方式了。
  • 提升java編程性能優化知識 程式設計師必看這幾點
    ,讓多個不相關的進程或線程之間實現通信。  2.儘量避免過多過常的創建Java對象  儘量避免在經常調用的方法,循環中new對象,由於系統不僅要花費時間來創建對象,而且還要花時間對這些對象進行垃圾回收和處理,在我們可以控制的範圍內,最大限度的重用對象,最好能用基本的數據類型或數組來替代對象。
  • (提高Java代碼質量)|25個優化Java代碼的小技巧
    反例:正例:5.字符串拼接使用 StringBuilder一般的字符串拼接在編譯期 java 會進行優化,但是在循環中字符串拼接,java 編譯期無法做到優化,所以需要使用 StringBuilder 進行替換。
  • 為什麼java中序列化的serialVersionUID總是無意義的?
    一、serialVersionUID的作用通過java進行網絡之間的數據傳輸是不能直接把對象進行傳的,需要在發送端把數據切分,在接收端對切分的數據進行重裝。這種切分和重裝的方式就叫做序列化。二、為什麼總是無意義的ID?java序列化中的serialVersionUID後面我們通常是1L、或者是xxxL。這些數字有什麼意義呢?為什麼我們總是需要這些無意義的ID。帶著這些問題我們一步一步來揭曉答案。
  • 大神詳解,這麼詳細的Java設計模式不收藏可惜了
    為什麼再看一遍設計模式,主要有幾個原因:***,很多優秀的源碼基本都使用了設計模式,明確設計模式能夠更好的看源碼。第二,很多中間件設計理念也是基於設計模式的,還有其他的語言,都有自己的設計優秀實踐。對於我來說,設計模式始於java,不止於java。第三,有了這種規範,可以更好的和他人溝通,言簡意賅。