溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何進(jìn)行Java字符串池String Pool的深度解析

發(fā)布時(shí)間:2021-12-28 14:21:12 來源:億速云 閱讀:134 作者:柒染 欄目:大數(shù)據(jù)

這篇文章給大家介紹如何進(jìn)行Java字符串池String Pool的深度解析,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。

      在工作中,String類是我們使用頻率非常高的一種對(duì)象類型。JVM為了提升性能和減少內(nèi)存開銷,避免字符串的重復(fù)創(chuàng)建,其維護(hù)了一塊特殊的內(nèi)存空間,這就是我們今天要討論的核心,即字符串池(String Pool)。字符串池由String類私有的維護(hù)。

      我們知道,在Java中有兩種創(chuàng)建字符串對(duì)象的方式:1)采用字面值的方式賦值  2)采用new關(guān)鍵字新建一個(gè)字符串對(duì)象。這兩種方式在性能和內(nèi)存占用方面存在著差別。

      方式一:采用字面值的方式賦值,例如:

      如何進(jìn)行Java字符串池String Pool的深度解析

      采用字面值的方式創(chuàng)建一個(gè)字符串時(shí),JVM首先會(huì)去字符串池中查找是否存在"aaa"這個(gè)對(duì)象,如果不存在,則在字符串池中創(chuàng)建"aaa"這個(gè)對(duì)象,然后將池中"aaa"這個(gè)對(duì)象的引用地址返回給字符串常量str,這樣str會(huì)指向池中"aaa"這個(gè)字符串對(duì)象;如果存在,則不創(chuàng)建任何對(duì)象,直接將池中"aaa"這個(gè)對(duì)象的地址返回,賦給字符串常量。

      在本例中,執(zhí)行:str == str2 ,會(huì)得到以下結(jié)果:

      如何進(jìn)行Java字符串池String Pool的深度解析

      這是因?yàn)?,?chuàng)建字符串對(duì)象str2時(shí),字符串池中已經(jīng)存在"aaa"這個(gè)對(duì)象,直接把對(duì)象"aaa"的引用地址返回給str2,這樣str2指向了池中"aaa"這個(gè)對(duì)象,也就是說str和str2指向了同一個(gè)對(duì)象,因此語句System.out.println(str == str2)輸出:true。

     方式二:采用new關(guān)鍵字新建一個(gè)字符串對(duì)象,例如:

     如何進(jìn)行Java字符串池String Pool的深度解析

     采用new關(guān)鍵字新建一個(gè)字符串對(duì)象時(shí),JVM首先在字符串池中查找有沒有"aaa"這個(gè)字符串對(duì)象,如果有,則不在池中再去創(chuàng)建"aaa"這個(gè)對(duì)象了,直接在堆中創(chuàng)建一個(gè)"aaa"字符串對(duì)象,然后將堆中的這個(gè)"aaa"對(duì)象的地址返回賦給引用str3,這樣,str3就指向了堆中創(chuàng)建的這個(gè)"aaa"字符串對(duì)象;如果沒有,則首先在字符串池中創(chuàng)建一個(gè)"aaa"字符串對(duì)象,然后再在堆中創(chuàng)建一個(gè)"aaa"字符串對(duì)象,然后將堆中這個(gè)"aaa"字符串對(duì)象的地址返回賦給str3引用,這樣,str3指向了堆中創(chuàng)建的這個(gè)"aaa"字符串對(duì)象。

     在這個(gè)例子中,執(zhí)行:str3 == str4,得到以下結(jié)果:

     如何進(jìn)行Java字符串池String Pool的深度解析

     因?yàn)?,采用new關(guān)鍵字創(chuàng)建對(duì)象時(shí),每次new出來的都是一個(gè)新的對(duì)象,也即是說引用str3和str4指向的是兩個(gè)不同的對(duì)象,因此語句System.out.println(str3 == str4)輸出:false。

     字符串池的實(shí)現(xiàn)有一個(gè)前提條件:String對(duì)象是不可變的。因?yàn)檫@樣可以保證多個(gè)引用可以同事指向字符串池中的同一個(gè)對(duì)象。如果字符串是可變的,那么一個(gè)引用操作改變了對(duì)象的值,對(duì)其他引用會(huì)有影響,這樣顯然是不合理的。

     字符串池的優(yōu)缺點(diǎn):字符串池的優(yōu)點(diǎn)就是避免了相同內(nèi)容的字符串的創(chuàng)建,節(jié)省了內(nèi)存,省去了創(chuàng)建相同字符串的時(shí)間,同時(shí)提升了性能;另一方面,字符串池的缺點(diǎn)就是犧牲了JVM在常量池中遍歷對(duì)象所需要的時(shí)間,不過其時(shí)間成本相比而言比較低。

     intern方法使用:一個(gè)初始為空的字符串池,它由類String獨(dú)自維護(hù)。當(dāng)調(diào)用 intern方法時(shí),如果池已經(jīng)包含一個(gè)等于此String對(duì)象的字符串(用equals(oject)方法確定),則返回池中的字符串。否則,將此String對(duì)象添加到池中,并返回此String對(duì)象的引用。 對(duì)于任意兩個(gè)字符串s和t,當(dāng)且僅當(dāng)s.equals(t)為true時(shí),s.instan() == t.instan才為true。所有字面值字符串和字符串賦值常量表達(dá)式都使用 intern方法進(jìn)行操作。

     GC回收:字符串池中維護(hù)了共享的字符串對(duì)象,這些字符串不會(huì)被垃圾收集器回收。

     Java語言規(guī)范(Java Language Specification)中對(duì)字符串做出了如下說明:每一個(gè)字符串常量都是指向一個(gè)字符串類實(shí)例的引用。字符串對(duì)象有一個(gè)固定值。字符串常量,或者一般的說,常量表達(dá)式中的字符串都被使用方法 String.intern進(jìn)行保留來共享唯一的實(shí)例。以上是Java語言規(guī)范中的原文,比較官方,用更通俗易懂的語言翻譯過來主要說明了三點(diǎn):1)每一個(gè)字符串常量都指向字符串池中或者堆內(nèi)存中的一個(gè)字符串實(shí)例;2)字符串對(duì)象值是固定的,一旦創(chuàng)建就不能再修改;3)字符串常量或者常量表達(dá)式中的字符串都被使用方法String.intern()在字符串池中保留了唯一的實(shí)例。并且給出了測(cè)試程序如下:

      如何進(jìn)行Java字符串池String Pool的深度解析

    編譯單元:

    如何進(jìn)行Java字符串池String Pool的深度解析

     輸出:

     如何進(jìn)行Java字符串池String Pool的深度解析

     這個(gè)例子說明了6點(diǎn):

  • 同一個(gè)包下同一個(gè)類中的字符串常量的引用指向同一個(gè)字符串對(duì)象;

  • 同一個(gè)包下不同的類中的字符串常量的引用指向同一個(gè)字符串對(duì)象;

  • 不同的包下不同的類中的字符串常量的引用仍然指向同一個(gè)字符串對(duì)象;

  • 由常量表達(dá)式計(jì)算出的字符串是在編譯時(shí)進(jìn)行計(jì)算,然后被當(dāng)作常量;

  • 在運(yùn)行時(shí)通過連接計(jì)算出的字符串是新創(chuàng)建的,因此是不同的;

  • 通過計(jì)算生成的字符串顯示調(diào)用intern方法后產(chǎn)生的結(jié)果與原來存在的同樣內(nèi)容的字符串常量是一樣的。

     從上面的例子可以看出,字符串常量在編譯時(shí)計(jì)算和在運(yùn)行時(shí)計(jì)算,其執(zhí)行過程是不同的,得到的結(jié)果也是不同的。我們來看看下面這段代碼:

     如何進(jìn)行Java字符串池String Pool的深度解析

     代碼輸出如下:

     如何進(jìn)行Java字符串池String Pool的深度解析

     為什么出現(xiàn)上面的結(jié)果呢?這是因?yàn)?,字符串字面量拼接操作是在Java編譯器編譯期間就執(zhí)行了,也就是說編譯器編譯時(shí),直接把"java"、"language"和"specification"這三個(gè)字面量進(jìn)行"+"操作得到一個(gè)"javalanguagespecification" 常量,并且直接將這個(gè)常量放入字符串池中,這樣做實(shí)際上是一種優(yōu)化,將3個(gè)字面量合成一個(gè),避免了創(chuàng)建多余的字符串對(duì)象。而字符串引用的"+"運(yùn)算是在Java運(yùn)行期間執(zhí)行的,即str + str2 + str3在程序執(zhí)行期間才會(huì)進(jìn)行計(jì)算,它會(huì)在堆內(nèi)存中重新創(chuàng)建一個(gè)拼接后的字符串對(duì)象??偨Y(jié)來說就是:字面量"+"拼接是在編譯期間進(jìn)行的,拼接后的字符串存放在字符串池中;而字符串引用的"+"拼接運(yùn)算實(shí)在運(yùn)行時(shí)進(jìn)行的,新創(chuàng)建的字符串存放在堆中。

     總結(jié):字符串是常量,字符串池中的每個(gè)字符串對(duì)象只有唯一的一份,可以被多個(gè)引用所指向,避免了重復(fù)創(chuàng)建內(nèi)容相同的字符串;通過字面值賦值創(chuàng)建的字符串對(duì)象存放在字符串池中,通過關(guān)鍵字new出來的字符串對(duì)象存放在堆中。

關(guān)于如何進(jìn)行Java字符串池String Pool的深度解析就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI