最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java字符串常量池和intern方法解析

 更新時(shí)間:2023年05月29日 08:40:15   作者:JL8  
本文主要介紹了Java字符串常量池和intern方法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

這篇文章,來(lái)討論一下Java中的字符串常量池以及Intern方法.這里我們主要討論的是jdk1.7,jdk1.8版本的實(shí)現(xiàn).

字符串常量池

在日常開(kāi)發(fā)中,我們使用字符串非常的頻繁,我們經(jīng)常會(huì)寫下類似如下的代碼:

String s = "abc";
String str = s + "def";

通常,我們一般不會(huì)這么寫:String s = new String("jkl"),但其實(shí)這么寫和上面的寫法還是有很多區(qū)別的.

先思考一個(gè)問(wèn)題,為什么要有字符串常量池這種概念?原因是字符串常量既然是不變的,那么完全就可以復(fù)用它,而不用再重新去浪費(fèi)空間存儲(chǔ)一個(gè)完全相同的字符串.字符串常量池是用于存放字符串常量的地方,在Java7和Java8中字符串常量池是堆的一部分.

假如我們有如下代碼:

String s = "abc";
String s1 = s + "def";    
String s2 = new String("abc");
String s3 = new String("def");

那么從內(nèi)存分配的角度上看,最終會(huì)有哪些字符串生成呢,首先我先給出一張圖來(lái)代表最終的結(jié)論,然后再分析一下具體的原因:

現(xiàn)在來(lái)依次分析上面的代碼的執(zhí)行流程:

執(zhí)行String s = "abc",此時(shí)遇到"abc"這個(gè)字符串常量,會(huì)在字符串常量池中完成分配,并且會(huì)將引用賦值給s,因此這條語(yǔ)句會(huì)在字符串常量池中分配一個(gè)"abc".(這里其實(shí)沒(méi)有空格,是因?yàn)樯晌恼聲r(shí)出現(xiàn)了空格,下文中如果出現(xiàn)同樣情況,請(qǐng)忽略空格)

執(zhí)行String s1 = s + "def",其實(shí)這個(gè)語(yǔ)句看似簡(jiǎn)單,實(shí)則另有玄機(jī),它其實(shí)最終編譯而成的代碼是這樣的:String s1 = new StringBuilder("abc").append("def").toString().首先在這個(gè)語(yǔ)句中有兩個(gè)字符串常量:"abc"和"def",所以在字符串常量池中應(yīng)該放置"abc"和"def",但是上個(gè)步驟已經(jīng)有"abc"了,所以只會(huì)放置"def".另外,new StringBuilder("abc")這個(gè)語(yǔ)句相當(dāng)于在堆上分配了一個(gè)對(duì)象,如果是new出來(lái)的,是在堆上分配字符串,是無(wú)法共享字符串常量池里面的字符串的,也就是說(shuō)分配到堆上的字符串都會(huì)有新的內(nèi)存空間. 最后toString()也是在堆中分配對(duì)象(可以從源碼中看到這個(gè)動(dòng)作),最終相當(dāng)于執(zhí)行了new String("abcdef");所以總結(jié)起來(lái),這條語(yǔ)句分析起來(lái)還是挺麻煩的,它分配了以下對(duì)象:

  • 在字符串常量池分配"abc",但本來(lái)就有一個(gè)"abc"了,所以不需要分配
  • 在字符串常量池中分配“def"
  • 在堆中分配了"abc"
  • 在堆中分配了"abcdef"

執(zhí)行String s2 = new String("abc").首先有個(gè)字符串常量"abc",需要分配到字符串常量池,但是字符串常量池中已經(jīng)有"abc"了,所以無(wú)需分配.因此new String("abc")最終在堆上分配了一個(gè)"abc".所以總結(jié)起來(lái)就是,在堆中分配了一個(gè)"abc"

執(zhí)行String s3 = new String("def");.首先有個(gè)字符串常量"def",需要分配到字符串常量池,但是字符串常量池中已經(jīng)有"def"了,所以無(wú)需分配.因此new String("def")最終在堆上分配了一個(gè)"def".所以總結(jié)起來(lái)就是,在堆中分配了一個(gè)"def"。

總結(jié)起來(lái),全部語(yǔ)句執(zhí)行后分配的對(duì)象如下:

  • 在堆中分配了兩個(gè)"abc",一個(gè)"abcdef",一個(gè)"def"
  • 在字符串常量池中分配了一個(gè)"abc",一個(gè)"def"

也就是圖中所表示的這些對(duì)象,如果明白了對(duì)象是如何分配的,我們就可以分析以下代碼的結(jié)果:

String s = "abc";
String s1 = s + "def";    
String s2 = new String("abc");
String s3 = new String("def");
String s4 = "abcdef";
String s5 = "abc";
System.out.println(s == s2); //false 前者引用的對(duì)象在字符串常量池 后者在堆上
System.out.println(s == s5);; //true 都引用了字符串常量池中的"abc"
System.out.println(s1 == s4); //false 前者引用的對(duì)象在字符串常量池,后者在堆上

intern方法

在字符串對(duì)象中,有一個(gè)intern方法.在jdk1.7,jdk1.8中,它的定義是如果調(diào)用這個(gè)方法時(shí),在字符串常量池中有對(duì)應(yīng)的字符串,那么返回字符串常量池中的引用,否則返回調(diào)用時(shí)相應(yīng)對(duì)象的引用,也就是說(shuō)intern方法在jdk1.7,jdk1.8中只會(huì)復(fù)用某個(gè)字符串的引用,這個(gè)引用可以是對(duì)堆內(nèi)存中字符串中的引用,也可能是對(duì)字符串常量池中字符串的引用.這里通過(guò)一個(gè)例子來(lái)說(shuō)明,假如我們有下面這段代碼:

String str = new String("abc");
String str2 = str.intern();
String str3 = new StringBuilder("abc").append("def").toString();
String str4 = str3.intern();
System.out.println(str == str2);
System.out.println(str3 == str4);   

那么str2和str以及str3和str4是否相等呢?如果理解了上面對(duì)字符串常量池的分析,那么我們可以明白在這段代碼中,字符串在內(nèi)存中是這么分配的:

  • 在堆中分配兩個(gè)"abc",一個(gè)“abcdef"
  • 在字符串常量池中分配一個(gè)"def",一個(gè)"abc"

當(dāng)執(zhí)行String str2 = str.intern();時(shí),會(huì)先從字符串常量池中尋找是否有對(duì)應(yīng)的字符串,此時(shí)在字符串常量池中有一個(gè)"abc",那么str2就指向字符串常量池中的"abc",而str是new出來(lái)的,指向的是堆中的"abc",所以str不等于str2;

當(dāng)執(zhí)行String str4 = str3.intern();會(huì)先從字符串常量池中尋找"abcdef",此時(shí)字符串常量池中并沒(méi)有"abcdef",因此str4會(huì)指向堆中的"abcdef",因此str3等于str4,我們會(huì)發(fā)現(xiàn)一個(gè)有意思的地方:如果將第三句改成String str3 = new StringBuilder("abcdef").toString();,也就是把a(bǔ)ppend后面的字符串和前面的字符串做一個(gè)拼接,那么結(jié)果就會(huì)變成str3不等于str4.所以這兩種寫法的區(qū)別還是挺大的.

要注意的是,在jdk1.6中intern的定義是如果字符串常量池中沒(méi)有對(duì)應(yīng)的字符串,那么就在字符串常量池中創(chuàng)建一個(gè)字符串,然后返回字符串常量池中的引用,也就是說(shuō)在jdk1.6中,intern方法返回的對(duì)象始終都是指向字符串常量池的.如果上面的代碼在jdk1.6中運(yùn)行,那么就會(huì)得到兩個(gè)false,原因如下:

  • 當(dāng)執(zhí)行String str2 = str.intern();時(shí),會(huì)先從字符串常量池中尋找是否有對(duì)應(yīng)的字符串,此時(shí)在字符串常量池中有一個(gè)"abc",那么str2就指向字符串常量池中的"abc",而str是new出來(lái)的,指向的是堆中的"abc",所以str不等于str2;
  • 當(dāng)執(zhí)行String str4 = str3.intern();會(huì)先從字符串常量池中尋找"abcdef",此時(shí)字符串常量池中并沒(méi)有"abcdef",因此執(zhí)行intern方法會(huì)在字符串常量池中分配"abcdef",然后str4最終等于這個(gè)字符串的引用,因此str3不等于str4,因?yàn)樯厦娴膕tr3指向堆,而str4指向字符串常量池,所以兩者一定不會(huì)相等.

在深入理解JVM虛擬機(jī)一書中,就有類似的代碼:

String str1 = new StringBuilder("計(jì)算機(jī)").append("軟件").toString();
System.out.println(str1 == str1.intern());
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str2 == str2.intern());

在jdk1.6中,兩個(gè)判斷都為false.因?yàn)閟tr1和str2都指向堆,而intern方法得出來(lái)的引用都指向字符串常量池,所以不會(huì)相等,和上面敘述的結(jié)論是一樣的.在jdk1.7中,第一個(gè)是true,第二個(gè)是false.道理其實(shí)也和上述所講的是一樣的,對(duì)于第一個(gè)語(yǔ)句,最終會(huì)在堆上創(chuàng)建一個(gè)"計(jì)算機(jī)軟件"的字符串,執(zhí)行str1.intern()方法時(shí),先在字符串常量池中尋找字符串,但沒(méi)有找到,所以會(huì)直接引用堆上的這個(gè)"計(jì)算機(jī)軟件",因此第一個(gè)語(yǔ)句會(huì)返回true,因?yàn)樽罱K都是指向堆.而對(duì)于第三個(gè)語(yǔ)句,因?yàn)楹偷谝粋€(gè)語(yǔ)句差不多,按理說(shuō)最終比較也應(yīng)該返回true.但實(shí)際上,str2.intern方法執(zhí)行的時(shí)候,在字符串常量池中是可以找到"java"這個(gè)字符串的,這是因?yàn)樵贘ava初始化環(huán)境去加載類的時(shí)候(執(zhí)行main方法之前),已經(jīng)有一個(gè)叫做"java"的字符串進(jìn)入了字符串常量池,因此str2.intern方法返回的引用是指向字符串常量池的,所以最終判斷的結(jié)果是false,因?yàn)橐粋€(gè)指向堆,一個(gè)指向字符串常量池.

總結(jié)

從上面的分析看來(lái),字符串常量池并不像是那種很簡(jiǎn)單的概念,要深刻理解字符串常量池,至少需要理解以下幾點(diǎn):

  • 理解字符串會(huì)在哪個(gè)內(nèi)存區(qū)域存放
  • 理解遇到字符串常量會(huì)發(fā)生什么
  • 理解new String或者是new StringBuilder產(chǎn)生的對(duì)象會(huì)在哪里存放
  • 理解字符串拼接操作+最終編譯出來(lái)的語(yǔ)句是什么樣子的
  • 理解toString方法會(huì)發(fā)生什么

這幾點(diǎn)都在本文章中覆蓋了,相信理解了這幾點(diǎn)之后一定對(duì)字符串常量池有一個(gè)更深刻的理解.其實(shí)這篇文章的編寫原因是因?yàn)殚喿x深入理解JVM虛擬機(jī)這本書的例子時(shí)突然發(fā)現(xiàn)作者所說(shuō)的和我所想的是不一樣的,但是書上可能對(duì)這方面沒(méi)有展開(kāi)敘述,所以我去查了點(diǎn)資料,然后寫了一些代碼來(lái)驗(yàn)證,最終決定寫一篇文章來(lái)記錄一下自己的理解,在編寫代碼過(guò)程中,還發(fā)現(xiàn)了一個(gè)分析對(duì)象內(nèi)存地址的類庫(kù),我放在參考資料中了.

參考資料

https://www.baeldung.com/java-object-memory-address查看java對(duì)象內(nèi)存地址

到此這篇關(guān)于Java字符串常量池和intern方法解析的文章就介紹到這了,更多相關(guān)Java字符串常量池和intern方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • EasyExcel核心實(shí)戰(zhàn)之Excel合并單元格,在線編輯與導(dǎo)出全攻略

    EasyExcel核心實(shí)戰(zhàn)之Excel合并單元格,在線編輯與導(dǎo)出全攻略

    在日常業(yè)務(wù)開(kāi)發(fā)中,Excel?報(bào)表三個(gè)字往往意味著復(fù)雜、凌亂和無(wú)限的加班,本文提供了一套完整的?EasyExcel?解決方案,涵蓋三大核心場(chǎng)景,?即合并單元格,在線編輯和數(shù)據(jù)導(dǎo)出,感興趣的小伙伴可以了解下
    2026-05-05
  • SpringBoot整合Apollo配置中心快速使用詳解

    SpringBoot整合Apollo配置中心快速使用詳解

    本文主要介紹了SpringBoot整合Apollo配置中心快速使用詳解,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • SpringMVC 數(shù)據(jù)校驗(yàn)方法(必看篇)

    SpringMVC 數(shù)據(jù)校驗(yàn)方法(必看篇)

    下面小編就為大家?guī)?lái)一篇SpringMVC 數(shù)據(jù)校驗(yàn)方法(必看篇)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-06-06
  • java中的類型擦除type?erasure示例詳解

    java中的類型擦除type?erasure示例詳解

    泛型是java從JDK?5開(kāi)始引入的新特性,泛型的引入可以讓我們?cè)诖a編譯的時(shí)候就強(qiáng)制檢查傳入的類型,從而提升了程序的健壯度,泛型可以用在類和接口上,在集合類中非常常見(jiàn),本文將會(huì)講解泛型導(dǎo)致的類型擦除
    2023-09-09
  • Spring中IOC和AOP的深入講解

    Spring中IOC和AOP的深入講解

    這篇文章主要給大家介紹了關(guān)于Spring中IOC和AOP的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-12-12
  • Java和Dubbo的SPI機(jī)制原理解析

    Java和Dubbo的SPI機(jī)制原理解析

    這篇文章主要介紹了Java和Dubbo的SPI機(jī)制原理解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • java mybatisplus批量新增和更新方式

    java mybatisplus批量新增和更新方式

    文章主要介紹使用Javamapper框架通過(guò)XML配置實(shí)現(xiàn)批量新增和更新操作,包含單字段更新(如刪除標(biāo)記)及多字段更新的方法,但內(nèi)容存在格式混亂問(wèn)題,需進(jìn)一步明確具體實(shí)現(xiàn)細(xì)節(jié)
    2025-09-09
  • Java實(shí)現(xiàn)獲取客戶端真實(shí)IP方法小結(jié)

    Java實(shí)現(xiàn)獲取客戶端真實(shí)IP方法小結(jié)

    本文給大家匯總介紹了2種使用java實(shí)現(xiàn)獲取客戶端真實(shí)IP的方法,主要用于獲取使用了代理訪問(wèn)的來(lái)訪者的IP,有需要的小伙伴可以參考下。
    2016-03-03
  • 詳解Java編程中的策略模式

    詳解Java編程中的策略模式

    這篇文章主要介紹了詳解Java編程中的策略模式,以及用策略模式來(lái)分析源碼等內(nèi)容,需要的朋友可以參考下
    2015-08-08
  • Maven發(fā)布封裝到中央倉(cāng)庫(kù)時(shí)候報(bào)錯(cuò):no default secret key

    Maven發(fā)布封裝到中央倉(cāng)庫(kù)時(shí)候報(bào)錯(cuò):no default secret key

    這篇文章主要介紹了Maven發(fā)布封裝到中央倉(cāng)庫(kù)時(shí)候報(bào)錯(cuò):no default secret key,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12

最新評(píng)論

江北区| 高安市| 滦平县| 思南县| 胶州市| 宁城县| 游戏| 余庆县| 松潘县| 岳阳市| 泌阳县| 大连市| 中超| 峨眉山市| 丁青县| 封丘县| 原阳县| 武汉市| 河东区| 永顺县| 海门市| 阜城县| 宜昌市| 庆阳市| 建瓯市| 内江市| 五原县| 商河县| 德保县| 洪洞县| 荣成市| 城固县| 沧源| 萝北县| 崇阳县| 石景山区| 赤壁市| 林州市| 钟山县| 平度市| 康平县|