日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

运行时常量池_从String的intern()到常量池

發(fā)布時(shí)間:2025/3/20 编程问答 14 豆豆
生活随笔 收集整理的這篇文章主要介紹了 运行时常量池_从String的intern()到常量池 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

在知乎上遇到一個(gè)剛學(xué)Java就接觸的字符串比較的問(wèn)題:?通常,根據(jù)"==比較的是地址,equals比較的是值"介個(gè)定理就能得到結(jié)果。但是String有些特殊,通過(guò)new String(string)生成的兩個(gè)同值的字符串地址就不相等,用其他方式來(lái)生成的兩個(gè)同值字符串地址就相等。

代碼如下:

// 第一種方式創(chuàng)建字符串,字面量賦值
String str1 = "abc";
String str2 = "abc";
// 第二種方式創(chuàng)建字符串
String str3 = new String("xyz");
String str4 = new String("xyz");
System.out.println(str1 == str2); //true
System.out.println(str3 == str4); //false

同樣是創(chuàng)建字符串,兩對(duì)等值的字符串進(jìn)行比較為什么結(jié)果不一樣,這就涉及到了常量池和堆。眾所周知,第一種方式創(chuàng)建的字符串,是將"abc"這個(gè)字面量放到了常量池中,然后str1和str2都指向常量池中的"abc",所以兩個(gè)變量地址相同;第二種方式創(chuàng)建的字符串,是先在常量池中放入"xyz",然后通過(guò)構(gòu)造函數(shù)將常量池中的"xyz"拷貝一份到堆中生成新的String,和常量池中的"xyx"就沒(méi)有了關(guān)系,所以兩個(gè)變量指向的是堆中兩個(gè)不同的變量,所以兩個(gè)變量地址不同。?

那intern()又是啥?和常量池之間又有什么聯(lián)系?

常量池

常量池是存放字面量、符號(hào)引用或直接引用的地方。而常量池又分為class常量池和運(yùn)行時(shí)常量池。

class常量池

class常量池是存放編譯期類中的字面量和符號(hào)引用。上面的字符串"abc"就是字面量;符號(hào)引用就是類和接口的完全限定名,字段的名稱和描述符,方法的名稱和描述符。

如圖:?圖中的就是new String(String)這個(gè)方法在常量池中的名稱和描述符,即符號(hào)引用。

運(yùn)行時(shí)常量池

我們平時(shí)說(shuō)的常量池指的就是運(yùn)行時(shí)常量池。在類加載的解析階段,會(huì)將class常量池載入內(nèi)存中(JDK1.7之前位于方法區(qū),現(xiàn)在位于Heap中),并且將符號(hào)引用解析成直接引用,即根據(jù)對(duì)方法/類的描述信息指向內(nèi)存中對(duì)應(yīng)的方法/類。運(yùn)行時(shí)常量池具有動(dòng)態(tài)性,可以在運(yùn)行期添加新的變量進(jìn)入常量池。

intern()

先看一下intern()這個(gè)方法的描述:用二級(jí)英文水平翻譯一波,大意就是一個(gè)string調(diào)用intern()的時(shí)候,如果池中有和這個(gè)字符串值相等的字符串對(duì)象,就會(huì)將字符串池中的字符串對(duì)象返回;如果沒(méi)有,就將這個(gè)字符串添加進(jìn)去,并返回這個(gè)字符串的引用。字符串池由String類私有維護(hù)。

這里又引入了字符串池這個(gè)概念。

字符串池

字符串池存放的是常量池中字符串對(duì)象的引用,而不是字符串對(duì)象。通過(guò)第一種字面量賦值法創(chuàng)建的字符串會(huì)放在常量池中,字符串池就會(huì)存儲(chǔ)這個(gè)字符串對(duì)象的引用,當(dāng)再次在常量池創(chuàng)建字符串時(shí),會(huì)先從字符串池查看是否有此字符串的等值引用,如果有的話,直接指向此引用對(duì)應(yīng)的對(duì)象。而第二種方式創(chuàng)建的字符串,會(huì)在字符串池中查找是否有與構(gòu)造參數(shù)等值的字符串,以此決定是否需要在常量池新建字符串,然后拷貝常量池中字符串在Heap創(chuàng)建一個(gè)新的字符串。?如圖,在堆中會(huì)在常量池中創(chuàng)建一個(gè)名為original的新字符串,然后拷貝并在堆中生成一個(gè)新字符串。注釋中也提到,除非你需要一個(gè)字符串的顯式副本,否則不需要使用這個(gè)構(gòu)造函數(shù),因?yàn)樽址遣豢勺兊摹?/p>

這里使用intern()測(cè)試一下字符串池:

public static void main(String[] args) {
//第一部分 測(cè)試
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1.intern() == str1); //true
System.out.println(str1.intern() == str2); //false
System.out.println(str1.intern() == str2.intern()); //true
//第二部分 測(cè)試通過(guò)char[]創(chuàng)建字符串后,引用是否會(huì)進(jìn)入字符串池
String str3 = new String(new char[]{'g', 'h'});
String str4 = "gh";
System.out.println(str3.intern() == str3); //false
System.out.println(str3.intern() == str4); //true
//第三部分 測(cè)試char[]創(chuàng)建的字符串調(diào)用intern()后引用是否進(jìn)入字符串池
String str3 = new String(new char[]{'g', 'h'});
str3.intern();
String str4 = "gh";
System.out.println(str3.intern() == str3); //true
System.out.println(str3.intern() == str4); //true
}

上面的三部分代碼是獨(dú)立測(cè)試。

第一部分:str1在常量池創(chuàng)建了abc,并將引用放入字符串池,str2拷貝常量池中的abc并在堆中創(chuàng)建新字符串。intern()從字符串池中獲取的是常量池中str1的abc引用。

第二部分:str3通過(guò)char[]在堆中創(chuàng)建了字符串,不是在常量池,所以gh的引用不會(huì)自動(dòng)放入字符串池。str4在常量池創(chuàng)建了gh,所以字符串池中保存了str4的gh引用。intern()從字符串池中獲取的是常量池中str4的gh引用。

第三部分:str3通過(guò)char[]在堆中創(chuàng)建了字符串,不是在常量池,所以gh的引用不會(huì)自動(dòng)放入字符串池,但是它調(diào)用intern()手動(dòng)將str3的gh的引用添加到了字符串池中。當(dāng)str4使用字面量賦值創(chuàng)建時(shí),查詢到字符串池中有g(shù)h的引用,str4就指向了str3的gh引用。intern()從字符串池中獲取的是堆中str3的gh引用。?

從上面的代碼中也得出結(jié)論:intern()可以將堆中創(chuàng)建的且字符串池沒(méi)有等值引用的字符串引用放入字符串池。

同時(shí),這也能說(shuō)明String為什么不可變這個(gè)問(wèn)題。因?yàn)檫@樣可以保證多個(gè)引用可以同時(shí)指向字符串池中的同一個(gè)對(duì)象。如果字符串是可變的,其中的一個(gè)引用操作改變了對(duì)象的值,對(duì)其他引用會(huì)有影響,這樣顯然是不可以的。

言歸正傳

回到知乎上的問(wèn)題。在常量池創(chuàng)建了"string"并將其引用放入字符串池,str1調(diào)用intern()返回的是常量池中的引用,而str1指向的是堆中的引用,所以輸出為false。

而StringBuilder的toString()是通過(guò)char[]創(chuàng)建字符串:?在堆中創(chuàng)建了abcdef之后,str2調(diào)用intern()將堆中引用放入字符串池并返回此引用,與str2指向堆中同一個(gè)字符串對(duì)象,所以輸出為true。

結(jié)語(yǔ)

Java中有時(shí)候很小的問(wèn)題也會(huì)發(fā)散出很多知識(shí)點(diǎn),不論是底層還是JVM的理論學(xué)習(xí),結(jié)合應(yīng)用案例會(huì)理解的更加深刻。就像文中提到的常量池就是class文件結(jié)構(gòu)和類加載理論學(xué)習(xí)的一部分。

總結(jié)

以上是生活随笔為你收集整理的运行时常量池_从String的intern()到常量池的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。