Java 字符串優化:詳解 String.intern() 方法
在Java編程中,字符串是最常用的數據類型之一,但是對于字符串的操作往往需要注意內存的使用和性能問題。本文我們將深入探討Java中的字符串優化技術,重點關注于String類的intern()方法,以及如何正確地使用它來優化字符串操作
今日內容介紹,大約花費9分鐘
圖片
昨天介紹了深入理解Java字符串常量池,介紹了String創建方法以及字符串常量池,今天介紹String一個方法String.intern(),大家可以看看美團寫的美團技術團隊深入解析 String.intern()文章,這是精品中精品,可是大家看了之后會覺得,我要放棄學習Java,因為有點看不懂,那么我簡化給大家講講,前提是小伙伴已經理解昨天深入理解Java字符串常量池這篇文章一下幾點:
- 1.使用雙引號聲明的字符串對象會保存在字符串常量池中
- 2.使用 new 關鍵字創建的字符串對象會先從字符串常量池中找,如果沒找到就創建一個,然后再在堆中創建字符串對象;如果找到了,就直接在堆中創建字符串對象
思考:new String("spring") + new String("葵花寶典");操作如何進行性能優化
1.String.intern()簡介
String str = new String("spring") + new String("葵花寶典");如果要把上面str內容存放到常量池,就需要使用intern()方法
注意:Java 7時,字符串常量池從永久代中移動到了堆中,但是永久代還沒有完全被移除。Java 8時,永久代被徹底移除。
Java 7之前和Java 7之后,String.intern()方法在執行時的策略發生了變化,這一變化直接影響了內存的利用方式
- 在Java 7之前,無論對象是否已經存在于堆中,String.intern()方法都會在字符串常量池中創建一個新的對象。
- Java 7之后,由于字符串常量池被移動到了堆中,執行String.intern()方法時,如果堆中已經存在了該對象,字符串常量池中就不會創建新的對象,而是直接保存堆中對象的引用。這一優化節省了一部分內存空間。
2. String.intern()舉例說明
可能小伙伴還沒有理解,那么別怕,通過以下例子進行說明
2.1. new String("spring葵花寶典")
String str1 = new String("spring葵花寶典");
String str2= str1.intern();
System.out.println(str1 == str2);思考:大家猜猜上面代碼結果是什么?可能小伙伴猜不出來,那么我直接來解釋一下
第一行,字符串常量池中會先創建一個spring葵花寶典的對象,然后堆中會再創建一個spring葵花寶典的對象,str1 引用的是堆中的對象。
第二行,str1 執行 intern() 方法,該方法會從字符串常量池中查找spring葵花寶典,如果常量池中存在spring葵花寶典字符串是否存在,因為第一行代碼已經在字符串常量池創建spring葵花寶典,所以 str2引用的是字符串常量池中的對象
圖片
image
str1 和 str2 的引用地址是不同的,str1一個來自堆,str2一個來自字符串常量池,所以輸出的結果為 false。
2.2. new String("spring") + new String("葵花寶典")
String str1 = new String("spring") + new String("葵花寶典");
String str2= str1.intern();
System.out.println(str1 == str2);思考:2.1輸出結果是false,那么2.2結果也是false?
可能小伙伴猜錯了,代碼輸出結果為true,這是為啥?
第一行,字符串常量池中會先創建兩個對象spring和葵花寶典,然后堆中會再創建兩個匿名對象spring和葵花寶典,最后還有一個spring葵花寶典對象(一會解釋),str1 引用的是堆中spring葵花寶典對象。
第二行,str1 執行 intern() 方法,該方法會從字符串常量池中查找spring葵花寶典對象是否存在,此時字符串常量池不存在,但是堆中存在
圖片
image
字符串常量池中保存的是堆中spring葵花寶典對象的引用,也就是說,str1 和 str2 的引用地址是相同,所以輸出的結果為 true
具體步驟如下:
- 創建 "spring" 字符串對象,存儲在字符串常量池中。
- 創建 "葵花寶典" 字符串對象,存儲在字符串常量池中。
- 執行 new String("spring"),在堆上創建一個字符串對象,內容為 "spring"。
- 執行 new String("葵花寶典"),在堆上創建一個字符串對象,內容為 "葵花寶典"。
- 執行 new String("spring") + new String("葵花寶典"),會創建一個 StringBuilder 對象,并將 "spring" 和 "葵花寶典" 追加到其中,然后調用 StringBuilder 對象的 toString() 方法,將其轉換為一個新的字符串對象,內容為 "spring葵花寶典"。這個新的字符串對象存儲在堆上。
特別說明:編譯器遇到 + 號這個操作符的時候,會將 new String("spring") + new String("葵花寶典") 編譯代碼如下:
new StringBuilder().append("spring").append("葵花寶典").toString();實際步驟如下:
- 創建一個 StringBuilder 對象。
- StringBuilder 對象上調用 append("spring"),將 "spring" 追加到 StringBuilder 中。
- 在 StringBuilder 對象上調用 append("葵花寶典"),將 "葵花寶典" 追加到 StringBuilder 中。
- 在 StringBuilder 對象上調用 toString() 方法,將 StringBuilder 轉換為一個新的字符串對象,內容為 "spring葵花寶典"
3. String.intern()使用注意事項
盡管String.intern()方法能夠有效地優化字符串操作,但是在使用時需要注意以下幾點:
- 不要濫用intern()方法: 雖然intern()方法可以確保所有具有相同內容的字符串共享相同的內存空間,但也不要隨意使用,因為字符串常量池是有大小限制的,過多的字符串可能會導致性能下降。
- 注意內存消耗: 使用intern()方法可能會增加內存消耗,因為它會將字符串對象存儲到常量池中,而常量池是位于堆內存中的一部分。


























