欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

這篇文章,來討論一下Java中的字符串常量池以及Intern方法.這里我們主要討論的是jdk1.7,jdk1.8版本的實現.

字符串常量池

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

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

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

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

假如我們有如下代碼:

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

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

現在來依次分析上面的代碼的執(zhí)行流程:

執(zhí)行String s = "abc",此時遇到"abc"這個字符串常量,會在字符串常量池中完成分配,并且會將引用賦值給s,因此這條語句會在字符串常量池中分配一個"abc".(這里其實沒有空格,是因為生成文章時出現了空格,下文中如果出現同樣情況,請忽略空格)

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

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

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

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

總結起來,全部語句執(zhí)行后分配的對象如下:

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

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

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 前者引用的對象在字符串常量池 后者在堆上
System.out.println(s == s5);; //true 都引用了字符串常量池中的"abc"
System.out.println(s1 == s4); //false 前者引用的對象在字符串常量池,后者在堆上

intern方法

在字符串對象中,有一個intern方法.在jdk1.7,jdk1.8中,它的定義是如果調用這個方法時,在字符串常量池中有對應的字符串,那么返回字符串常量池中的引用,否則返回調用時相應對象的引用,也就是說intern方法在jdk1.7,jdk1.8中只會復用某個字符串的引用,這個引用可以是對堆內存中字符串中的引用,也可能是對字符串常量池中字符串的引用.這里通過一個例子來說明,假如我們有下面這段代碼:

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是否相等呢?如果理解了上面對字符串常量池的分析,那么我們可以明白在這段代碼中,字符串在內存中是這么分配的:

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

當執(zhí)行String str2 = str.intern();時,會先從字符串常量池中尋找是否有對應的字符串,此時在字符串常量池中有一個"abc",那么str2就指向字符串常量池中的"abc",而str是new出來的,指向的是堆中的"abc",所以str不等于str2;

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

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

  • 當執(zhí)行String str2 = str.intern();時,會先從字符串常量池中尋找是否有對應的字符串,此時在字符串常量池中有一個"abc",那么str2就指向字符串常量池中的"abc",而str是new出來的,指向的是堆中的"abc",所以str不等于str2;
  • 當執(zhí)行String str4 = str3.intern();會先從字符串常量池中尋找"abcdef",此時字符串常量池中并沒有"abcdef",因此執(zhí)行intern方法會在字符串常量池中分配"abcdef",然后str4最終等于這個字符串的引用,因此str3不等于str4,因為上面的str3指向堆,而str4指向字符串常量池,所以兩者一定不會相等.

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

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

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

總結

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

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

這幾點都在本文章中覆蓋了,相信理解了這幾點之后一定對字符串常量池有一個更深刻的理解.其實這篇文章的編寫原因是因為閱讀深入理解JVM虛擬機這本書的例子時突然發(fā)現作者所說的和我所想的是不一樣的,但是書上可能對這方面沒有展開敘述,所以我去查了點資料,然后寫了一些代碼來驗證,最終決定寫一篇文章來記錄一下自己的理解,在編寫代碼過程中,還發(fā)現了一個分析對象內存地址的類庫,我放在參考資料中了.

參考資料

https://www.baeldung.com/java-object-memory-address查看java對象內存地址

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

相關文章

  • SpringBoot實現微信支付接口調用及回調函數(商戶參數獲取)

    SpringBoot實現微信支付接口調用及回調函數(商戶參數獲取)

    本文詳細介紹了使用SpringBoot實現微信支付接口調用及回調函數的步驟,提供了代碼實現的具體步驟和工具類的創(chuàng)建,感興趣的朋友跟隨小編一起看看吧
    2024-11-11
  • Java8的Lambda遍歷兩個List匹配數據方式

    Java8的Lambda遍歷兩個List匹配數據方式

    這篇文章主要介紹了Java8的Lambda遍歷兩個List匹配數據方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • OpenCV在Android上的應用示例

    OpenCV在Android上的應用示例

    這篇文章主要介紹了OpenCV在Android上的應用示例,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04
  • 關于JDBC的簡單封裝(實例講解)

    關于JDBC的簡單封裝(實例講解)

    下面小編就為大家?guī)硪黄P于JDBC的簡單封裝(實例講解)。小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-08-08
  • 如何修改logback.xml配置文件在resource以外的位置

    如何修改logback.xml配置文件在resource以外的位置

    這篇文章主要介紹了如何修改logback.xml配置文件在resource以外的位置,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • springboot配合Thymeleaf完美實現遍歷功能

    springboot配合Thymeleaf完美實現遍歷功能

    Thymeleaf顯然是一個開發(fā)頁面的技術,現在各種前端技術層出不窮,比如現在主流的Vue、React、AngularJS等。這篇文章主要介紹了springboot配合Thymeleaf完美實現遍歷,需要的朋友可以參考下
    2021-09-09
  • Java多線程生產者消費者模式實現過程解析

    Java多線程生產者消費者模式實現過程解析

    這篇文章主要介紹了Java多線程生產者消費者模式實現過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-03-03
  • Java反射之Call stack introspection詳解

    Java反射之Call stack introspection詳解

    這篇文章主要介紹了Java反射之Call stack introspection詳解,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • 教你怎么用Java開發(fā)掃雷游戲

    教你怎么用Java開發(fā)掃雷游戲

    我們那時候上機經常玩掃雷,試想如果我當年可以用 java 寫個掃雷出來,那場面不用我多說了吧,大家讓開,我要開始裝逼了,之前用JavaScript寫過了一個掃雷,這次我用java再寫了一遍,權當是復習咯.文中有非常詳細的代碼示例,需要的朋友可以參考下
    2021-05-05
  • Java操作透明圖片并保持背景透明的實現

    Java操作透明圖片并保持背景透明的實現

    這篇文章主要介紹了Java操作透明圖片并保持背景透明的實現,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-11-11

最新評論