您好,登錄后才能下訂單哦!
今天給大家?guī)淼氖窃诿嬖囍薪?jīng)常被問到的一道題:
無論在Java還是Android中,String是一個(gè)很常見的類,但是大家真的很了解嗎,我這里有幾個(gè)題:
1.
String str1 = "abc"; String str2 = new String("abc");
這兩種創(chuàng)建String對象的方法有什么不同?
2.
String s = "a" + "b" + "c" + "d";
這里面一共創(chuàng)建了多少對象?
這兩道題昨天給筆者搞得是一臉懵逼,后來一聽這是一道很經(jīng)典的面試題,就趕快查閱各種資料,現(xiàn)在已經(jīng)解決了。
在解決這兩個(gè)題之前,我們先來明確幾個(gè)知識點(diǎn),相信把這幾個(gè)知識點(diǎn)弄完之后再回頭看這兩個(gè)題,就很簡單了:
我們首先來開第一個(gè):
引用在棧內(nèi)存中存儲,對象在堆內(nèi)存中存儲。
這個(gè)是我粗略畫的一張圖,這張圖可能不是很準(zhǔn)確,但是我只想表達(dá)一個(gè)意思,我們的棧內(nèi)存,存放的是我們對象的引用和我們基本數(shù)據(jù)類型的值。而堆內(nèi)存中存放的是我們的對象。就是這么簡單。
為什么說String對象不可變
這里我們用一下大佬的圖:
如果用代碼表示上面的圖,那就是:
String s = "abcd"; s = s + "el";
如果沒有上面這張圖,大家可能會(huì)覺得我們只是給s對象后面加了一個(gè)el,這不還是原來的s嗎?不,我們說的String對象不可變就是這個(gè)意思,當(dāng)我們給s再加上el時(shí)候,新的字符串a(chǎn)bcdel被存放到了一個(gè)新的內(nèi)存中。已經(jīng)不是原來的內(nèi)存了。所以說String對象不可變,如果變了,那就已經(jīng)變成了一個(gè)新的對象。
那說到這里大家可能會(huì)返回去看我寫的第二個(gè)問題:按你的說法這不就7個(gè)對象嗎?abcd各占一個(gè),每次+到一起都要重新生成對象,一共生成了7個(gè)對象。話是沒錯(cuò),但是我想說這道題說多解,等我們講完了下面這兩個(gè)知識點(diǎn),這道題就完全解開了。
String創(chuàng)建對象的形式:
正如我們上面看到的,String有兩種創(chuàng)建對象的形式:
1.字面量形式:
String str = "asd";
2.標(biāo)準(zhǔn)的new形式:
String str = new String("asd");
字符串常量池的意義:
字符串常量池,又稱為字符串在字面量池。大家不要他想象的多么高深額,其實(shí)說白了他就是一塊內(nèi)存,它里面存放的是我們的字符串的引用。
大家可能疑問這個(gè)東西有什么意義:假如我們要?jiǎng)?chuàng)建一個(gè)字符串,"a" + "b" + "c" + ....+ 我們+了一萬次,那么按照上面的說法,我們是不是為了創(chuàng)建一個(gè)很長的字符串,創(chuàng)建了很多的對象。這樣不但有的字符串被重復(fù)的創(chuàng)建了,而且占用了很多不必要的內(nèi)存,代價(jià)有點(diǎn)大。
我們的JVM為了減少字符串的重復(fù)創(chuàng)建,維護(hù)了一個(gè)特殊的內(nèi)存:就是這個(gè)字符串常量池。
在這個(gè)字符串常量池中,存放著我們字符串對象的引用。下面我們根據(jù)字符串創(chuàng)建對象的兩種形式來說明一下常量池是如何存放String對象引用的:
1.通過字面量創(chuàng)建String對象:
我們舉個(gè)例子,String str = "abc";我們通過字面量形式創(chuàng)建一個(gè)值為"abc"的對象,首先我們判斷常量池中是否存在一個(gè)引用,這個(gè)引用的值也是"abc",如果有這個(gè)值,那么我們就從常量池中拿到這個(gè)引用,返回給str。
如果沒有,那么我們就創(chuàng)建這個(gè)對象,然后把str這個(gè)引用值放到常量池中。
在這里我們要明確幾個(gè)點(diǎn)了:
2.通過new創(chuàng)建String對象。
這里其實(shí)只有一句話,無論常量池中是否存在相同值的引用,至少創(chuàng)建一個(gè)對象。因?yàn)椴还軇e的,new這個(gè)語法就一定會(huì)創(chuàng)建一個(gè)新的對象,然后我們要看構(gòu)造方法中的字符串,如果常量池中存在與該字符串相同值的引用,那么就不會(huì)創(chuàng)建新的對象,反之會(huì)創(chuàng)建對象之后,在常量池中存放這個(gè)引用。
這樣看起來我們的常量池的意義就很明確了,減少重復(fù)創(chuàng)建String對象,從而減少不必要的內(nèi)存消耗。
如果要說他的弊端的話,應(yīng)該就是犧牲了CPU的計(jì)算時(shí)間來換取空間吧,因?yàn)椴檎沂欠裼邢嗤瑑?nèi)容的引用是CPU耗時(shí)計(jì)算,但是與前者占用內(nèi)存相比,這個(gè)還是算不了什么的吧(筆者自己觀點(diǎn),沒有官方支持哈哈)。
intern()方法使用
最后我們來看一下這個(gè)intern()方法,這個(gè)方法其實(shí)理解為查看常量池中是否存在對應(yīng)內(nèi)容的引用。如果有他會(huì)返回常量池中的引用,如果沒有則會(huì)將當(dāng)前String的引用放入常量池。
我們舉幾個(gè)例子對比下就好:
public static void main(String[] args) { String str1 = "gfzy"; String str2 = str1.intern(); System.out.println(str1 == str2); }
他的結(jié)果是true。很簡單,我們首先通過字面量形式創(chuàng)建了一個(gè)“gfzy”對象,此時(shí)常量池中沒有相同內(nèi)容引用,所以常量池存放str1的引用。
然后str2為str1的.intern()方法,現(xiàn)在常量池中存在“gfzy”這個(gè)內(nèi)容的引用,所以我們返回這個(gè)引用,也就是str1,所以str1和str2指向同一個(gè)引用。
public static void main(String[] args) { String str1 = new String("gfzy"); String str2 = "gfzy"; System.out.println(str1 == str2); }
這個(gè)結(jié)果為false,首先我們看到str1是new了一個(gè)對象,所以他肯定是在堆內(nèi)存中一塊新的內(nèi)存,而構(gòu)造方法中的“gfzy”,首先在常量池中會(huì)拿到他的引用,然后返回這個(gè)引用。
str2直接拿到了常量池中的引用,所以一個(gè)是堆內(nèi)存新創(chuàng)建的,一個(gè)是原來常量池中的引用,兩者指向不是一個(gè)內(nèi)存,所以是false。
而如果是這樣呢:
public static void main(String[] args) { String str1 = new String("gfzy").intern(); String str2 = "gfzy"; System.out.println(str1 == str2); }
這個(gè)結(jié)果為true。在原來的基礎(chǔ)上只是添加了一個(gè)inter方法,在new String("gfzy");時(shí)候,現(xiàn)在常量池中已經(jīng)有了這個(gè)引用。而現(xiàn)在intern,我們會(huì)拿到常量池中的引用,所以str1為常量池中的引用,str2和上面一樣,所以返回true。
現(xiàn)在這幾個(gè)問題都解決完了,我們再返回頭看之前的兩個(gè)問題,第一個(gè)問題就很簡單了,而我們剛才也說過了。
第二個(gè)是看常量池中是否已經(jīng)存在了對應(yīng)的引用,如果沒有,那么是"a","b","ab","c","abc","d","abcd"七個(gè)對象,如果常量池中存在引用,那么只創(chuàng)建了一個(gè)“abcd”(這個(gè)是編譯器優(yōu)化后的結(jié)果,其實(shí)筆者這里也是有點(diǎn)懵逼,個(gè)人感覺應(yīng)該是3個(gè)額,希望這個(gè)問題有讀者多多留言,幫我解答一下這個(gè)問題)。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對億速云的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。