String 对象内存分配策略
這個(gè)問題可以說是一個(gè)高頻的面試題目,以前把這個(gè)問題弄懂了,最近突然想到這個(gè)問題,一時(shí)間竟然沒有太好的思路了。所以花些時(shí)間整理一下其中的知識點(diǎn)。
一、內(nèi)存分配策略
我們先來看一個(gè)題目(這個(gè)問題都快看吐了~),你知道正確的運(yùn)行結(jié)果并給出解釋嗎。不知道也沒關(guān)系,我會在下面給出具體的分析。
@Testpublic void test() {String s1 = "abc";String s2 = "abc";String s3 = new String("abc");String s4 = new String("abc");System.out.println(s1 == s2); // trueSystem.out.println(s3 == s4); // falseSystem.out.println(s1 == s3); // false}String s = "" 與 String s = new String("") 兩種方式都可以創(chuàng)建字符串對象,它們有什么不同嗎。下面的解釋會涉及到 java 虛擬機(jī)內(nèi)存區(qū)域 方面的知識,不知道的同學(xué)可以先了解一下。
1.1 創(chuàng)建對象的方式
String s = "abc" 方式創(chuàng)建的對象,存儲在字符串常量池中,在創(chuàng)建字符串對象之前,會先在常量池中檢查是否存在 abc 對象。如果存在,則直接返回常量池中 abc對象的引用,不存在會創(chuàng)建該對象,并將該對象的引用返回給對象 s。
以 HotSpot 虛擬機(jī)為例,在 jdk1.8 之前,字符串常量池在方法區(qū)中,為了減小方法區(qū)內(nèi)存溢出的風(fēng)險(xiǎn),在 jdk1.8 之后就把字符串常量池轉(zhuǎn)移到 java 堆中了。
String s = new String("abc") 這種方式,實(shí)際上 abc 本身就是字符串池中的一個(gè)對象,在運(yùn)行 new String() 時(shí),把字符串常量池中的字符串 abc 復(fù)制到堆中,因此該方式不僅會在堆中,還會在常量池中創(chuàng)建 abc 字符串對象。 最后把 java 堆中對象的引用返回給 s。
1.2 問題分析
通過上面的分析,大家應(yīng)該對問題結(jié)果有一個(gè)感性的認(rèn)識了。
s1 指向字符串常量池中的 abc 對象,s2 也指向字符串常量池中的 abc 對象,因此 s1 與 s2 指向的是同一個(gè)對象,故 s1 == s2 返回 true。
s3 與 s4 分別指向 java 堆中兩個(gè)不同的對象,因此 s3 == s4 是 false。
s1 與 s3 分別指向字符串常量池中的對象與 java 堆中的對象,s1 == s3 返回值也是 false。
二、問題擴(kuò)展
上面的問題清楚了原理還很好理解,下面還有一個(gè)例子,理解起來就不那么容易了。
@Testpublic void test() {String s1 = "abc";String s2 = "a";System.out.println(s1 == ("a" + "bc")); // trueSystem.out.println(s1 == (s2 + "bc")); // false }2.1 字符串常量重載 "+"
"a" + "bc" 是兩個(gè)字符串常量的拼接,當(dāng)一個(gè)字符串由多個(gè)字符串常量連接而成時(shí),它自己也肯定是字符串常量。java 虛擬機(jī)對于字符串常量的 “+” 號連接,在程序編譯期,java 虛擬機(jī)就將常量字符串的 “+” 連接優(yōu)化為連接后的值。
這樣一來,最終只會在常量池中創(chuàng)建一個(gè) abc 對象,并沒有創(chuàng)建臨時(shí)字符串對象 a 和 bc,減輕了垃圾收集器的壓力。s1 == ("a" + "bc"),因?yàn)?abc(s1) 在字符串常量池中已經(jīng)存在了,因此返回值是 true。
2.2 字符串引用重載 "+"
java 虛擬機(jī)對于有字符串引用存在的字符串連接,即 s2 + "bc" 在被編譯器執(zhí)行的時(shí)候,會自動引入 StringBuilder 對象,調(diào)用其 append() 方法,最終調(diào)用 toString() 方法返回其在堆中對象的引用。 下面是反編譯后的部分字節(jié)碼。
因此 s2 + "bc" 就等同于下面過程。
s2 + "bc" = new StringBuilder().append(s2).append("bc").toString();s1 == (s2 + "bc") 在進(jìn)行比較時(shí),s1 是字符串常量池中對象的引用,(s2 + "bc") 則是 java 堆中一個(gè)對象的引用,因此返回值是 false。為什么說 s2 + "bc" 返回的是堆中對象的引用呢,只要到 StringBuilder 源碼中查看一下 toString() 就能理解了。
/*** StringBuilder 類的 toSTring() 方法*/@Overridepublic String toString() {// Create a copy, don't share the arrayreturn new String(value, 0, count);}三、參考資料
- String 對象內(nèi)存分配 (常量池和堆的分配)
- Java—String 字符串運(yùn)算符"+"重載分析
總結(jié)
以上是生活随笔為你收集整理的String 对象内存分配策略的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 怎么开通信用卡 提前准备好这些一定没错
- 下一篇: 芝麻信用分700的人多吗 怎么样才能提