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

JDK8中String的intern()方法實例詳細解讀

 更新時間:2022年09月22日 10:53:50   作者:小王寫博客  
String字符串在我們日常開發(fā)中最常用的,當然還有他的兩個兄弟StringBuilder和StringBuilder,接下來通過本文給大家介紹JDK8中String的intern()方法詳細解讀,需要的朋友可以參考下

一、前言

String字符串在我們日常開發(fā)中最常用的,當然還有他的兩個兄弟StringBuilder和StringBuilder。他三個的區(qū)別也是面試中經常問到的,大家如果不知道,就要先去看看了哈!最近也是看周志明老師的深入JVM一書中寫到關于intern()方法的介紹,小編也是以前沒在開發(fā)中用到。但是面試題還是很多的,所以特意研究了一天,寫下來記錄一下自己的收獲,希望也可以幫助到大家!!

二、圖文理解String創(chuàng)建對象

1.例子一

String str1 = "wang";

JVM在編譯階段會判斷字符串常量池中是否有 "wang" 這個常量對象如果有,str1直接指向這個常量的引用,如果沒有會在常量池里創(chuàng)建這個常量對象。

2.例子二

String str2 = "學" + "Java";

JVM編譯階段過編譯器優(yōu)化后會把字符串常量直接合并成"學Java",所有創(chuàng)建對象時只會在常量池中創(chuàng)建1個對象。

3.例子三

String str3 = new String("學Java");

當代碼執(zhí)行到括號中的"學Java"的時候會檢測常量池中是否存在"學Java"這個對象,如果不存在則在字符串常量池中創(chuàng)建一個對象。當整行代碼執(zhí)行完畢時會因為new關鍵字在堆中創(chuàng)建一個"學Java"對象,并把棧中的變量"str3"指向堆中的對象,如下圖所示。這也是為什么說通過new關鍵字在大部分情況下會創(chuàng)建出兩個字符串對象!

4.例子四

String str4 = "學Java";
String str5 = "學Java";
System.out.println(str4 == str5); // 如下圖得知為:true

第一行代碼:
JVM在編譯階段會判斷字符串常量池中是否有 "學Java" 這個常量對象如果有,str4直接指向這個常量的引用,如果沒有會在常量池里創(chuàng)建這個常量對象。
第二行代碼:
再創(chuàng)建"學Java",發(fā)現字符串常量池中存在了"學Java",所以直接將棧中的str5變量也指向字符串常量池中已存在的"學Java"對象,從而避免重復創(chuàng)建對象,這也是字符串常量池存在的原因。

5.例子五

String str6 = new String("學") + new String("Java");

首先,會先判斷字符串常量池中是否存在"學"字符串對象,如果不存在則在字符串常量池中創(chuàng)建一個對象。當執(zhí)行到new關鍵字在堆中創(chuàng)建一個"學"字符串對象。后面的new String("Java"),也是這樣。
然后,當右邊完成時,會在堆中創(chuàng)建一個"學Java"字符串對象。并把棧中的變量"str6"指向堆中的對象。
總結:一句代碼創(chuàng)建了5個對象,但是有兩個在堆中是沒有引用的,按照垃圾回收的可達性分析,他們是垃圾就是"學"、"Java"這倆垃圾

心得:
上面代碼進行反編譯:

String str6 = (new StringBuilder()).append(new String("\u5B66"))
					.append(new String("Java")).toString();

底層是一個StringBuilder在進行把兩個對象拼接在一起,最后棧中str6指向堆中的"學Java",其實是StringBuilder對象。

6.例子六

String str7 = new String("學Java");
String str8 = new String("學Java");
System.out.println(str7 == str8); // 如下圖得知為:false

執(zhí)行到第一行:
執(zhí)行到括號內的"學Java",會先判斷字符串常量池中是否存在"學Java"字符串對象,如果沒有則在字符串常量池中創(chuàng)建一個"學Java"字符串對象,執(zhí)行到new關鍵字時,在堆中創(chuàng)建一個"學Java"字符串對象,棧中的變量str7的引用指向堆中的"學Java"字符串對象。
執(zhí)行到第二行:
當執(zhí)行到第二行括號中的"學Java"時,先判斷常量池中是否有"學Java"字符串對象,因為第一行代碼已經將其創(chuàng)建,所以有的話就不創(chuàng)建了;執(zhí)行到new關鍵字時,在堆中創(chuàng)建一個"學Java"字符串對象,棧中的變量str8的引用指向堆中的"學Java"字符串對象。

三、深入理解intern()方法

1. 源碼查看

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    // ....
    /**
     * Returns a canonical representation for the string object.
     * <p>
     * A pool of strings, initially empty, is maintained privately by the
     * class {@code String}.
     * <p>
     * When the intern method is invoked, if the pool already contains a
     * string equal to this {@code String} object as determined by
     * the {@link #equals(Object)} method, then the string from the pool is
     * returned. Otherwise, this {@code String} object is added to the
     * pool and a reference to this {@code String} object is returned.
     * <p>
     * It follows that for any two strings {@code s} and {@code t},
     * {@code s.intern() == t.intern()} is {@code true}
     * if and only if {@code s.equals(t)} is {@code true}.
     * <p>
     * All literal strings and string-valued constant expressions are
     * interned. String literals are defined in section 3.10.5 of the
     * <cite>The Java&trade; Language Specification</cite>.
     *
     * @return  a string that has the same contents as this string, but is
     *          guaranteed to be from a pool of unique strings.
     */
    public native String intern();
}

翻譯過來就是,當intern()方法被調用的時候,如果字符串常量池中已經存在這個字符串對象了,就返回常量池中該字符串對象的地址;如果字符串常量池中不存在,就在常量池中創(chuàng)建一個指向該對象堆中實例的引用,并返回這個引用地址。

2. 例子一

我們直接先把周志明老師的在深入JVM一書的例子:

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

這段代碼在JDK 6中運行,會得到兩個false,而在JDK 7、8中運行,會得到一個true和一個false。產 生差異的原因是,在JDK 6中,intern()方法會把首次遇到的字符串實例復制到永久代的字符串常量池 中存儲,返回的也是永久代里面這個字符串實例的引用,而由StringBuilder創(chuàng)建的字符串對象實例在 Java堆上,所以必然不可能是同一個引用,結果將返回false。 而JDK 7(以及部分其他虛擬機,例如JRockit)的intern()方法實現就不需要再拷貝字符串的實例到永久代了,既然字符串常量池已經移到Java堆中,那只需要在常量池里記錄一下首次出現的實例引用即可,因此intern()返回的引用和由StringBuilder創(chuàng)建的那個字符串實例就是同一個。而對str2比較返 回false,這是因為“java”(下面解釋)這個字符串在執(zhí)行String-Builder.toString()之前就已經出現過了,字符串常量 池中已經有它的引用,不符合intern()方法要求“首次遇到”的原則,“計算機軟件”這個字符串則是首次出現的,因此結果返回true。

java為什么已經存在了?

1.我們在一個類中輸入System,然后點擊到這個方法中,方法內容如下:

public final class System {
	// ...
	private static void initializeSystemClass() {
		// ...
		sun.misc.Version.init();
		// ...
	}
	// ...
}

2.我們點擊上面的Version類,類內容如下:

public class Version {
    private static final String launcher_name = "java";
    private static final String java_version = "1.8.0_121";
    private static final String java_runtime_name = "Java(TM) SE Runtime Environment";
    private static final String java_profile_name = "";
    private static final String java_runtime_version = "1.8.0_121-b13";
    private static boolean versionsInitialized;
    private static int jvm_major_version;
    private static int jvm_minor_version;
    private static int jvm_micro_version;
    private static int jvm_update_version;
    private static int jvm_build_number;
    private static String jvm_special_version;
    private static int jdk_major_version;
    private static int jdk_minor_version;
    private static int jdk_micro_version;
    private static int jdk_update_version;
    private static int jdk_build_number;
    private static String jdk_special_version;
    private static boolean jvmVersionInfoAvailable;

    public Version() {
    }

    public static void init() {
        System.setProperty("java.version", "1.8.0_121");
        System.setProperty("java.runtime.version", "1.8.0_121-b13");
        System.setProperty("java.runtime.name", "Java(TM) SE Runtime Environment");
    }
}

3.找到java關鍵字,所以上面的str2.intern() == str2返回false。

private static final String launcher_name = "java";

我們開始例子和詳細解釋,發(fā)車了,大家坐好哦!
以下例子來自:原博客,解釋是為小編自己的理解。

3. 例子二

String str1 = new String("wang");
str1.intern();
String str2 = "wang";
System.out.println(str1 == str2); // false

執(zhí)行第一行代碼:
首先執(zhí)行到"wang",因為字符串常量池中沒有,則會在字符串常量池中創(chuàng)建"wang"字符串對象。
然后執(zhí)行到new關鍵字時,在堆中創(chuàng)建一個"wang"的對象,并把棧中的str1的引用指向"wang"對象。

執(zhí)行第二行代碼:
這里我們看到就是str1手動把"wang"放在字符串常量池中,但是發(fā)現字符串常量池中已經存在"wang"字符串對象,所以直接把已存在的引用返回。雖然str1.intern()指向了字符串常量池中的"wang",但是我們第四行代碼并沒有拿str1.intern()作比較,所以還是false。

執(zhí)行第三行代碼:
首先通過第一行代碼,字符串常量池中已經有"wang"字符串對象了,所以本行代碼只需要把棧中的str2變量指向字符串常量池中的"wang"即可。

執(zhí)行第四行代碼:
如上和下圖可見,我們的str1執(zhí)行堆中的"wang",str2指向的是字符串常量池中的"wang",肯定返回false。

4. 例子三

String str3 = new String("wang") + new String("zhen");
str3.intern();
String str4 = "wangzhen";
System.out.println(str3 == str4); // true

執(zhí)行到第一行代碼:
首先執(zhí)行到"wang"時,因為字符串常量池中沒有,則會在字符串常量池中創(chuàng)建一個"wang"字符串對象;
然后執(zhí)行到"zhen"時,因為字符串常量池中沒有,則會在字符串常量池中創(chuàng)建一個"zhen"字符串對象;
最后執(zhí)行到new關鍵字時,看到是兩個,但是底層字節(jié)碼文件反編譯的是使用如下可見,只會有一個StringBuilder對象生成,于是將棧中的str3的引用指向堆中的"wangzhen"對象。

String str3 = (new StringBuilder()).append(new String("wang"))
					.append(new String("zhen")).toString();

執(zhí)行到第二行代碼:
這里我們看到就是str3手動把"wangzhen"放在字符串常量池中,在字符串常量池中沒有找到"wangzhen",于是把str3 .intern()引用指向堆中的"wangzhen"的地址。現在str3和str3 .intern()一樣

執(zhí)行到第三行代碼:
判斷字符串常量池中是否存在"wangzhen"字符串對象,第二行代碼已經在字符串常量池中創(chuàng)建了"wangzhen",不過str4是指向str3中堆的引用(看圖就明白了)。

執(zhí)行到第四行代碼:
str3和str3 .intern()引用一樣,str3 .intern()和str4是一個,所以str3和str4相等。

5. 例子四

String str5 = new String("wang") + new String("zhen");
String str6 = "wangzhen";
str5.intern();
System.out.println(str5 == str6); // false

執(zhí)行到第一行代碼:
首先執(zhí)行到"wang"時,因為字符串常量池中沒有,則會在字符串常量池中創(chuàng)建一個"wang"字符串對象;
然后執(zhí)行到"zhen"時,因為字符串常量池中沒有,則會在字符串常量池中創(chuàng)建一個"zhen"字符串對象;
最后執(zhí)行到new關鍵字時,看到是兩個,但是底層字節(jié)碼文件反編譯的是使用如下可見,只會有一個StringBuilder對象生成,于是將棧中的str5的引用指向堆中的"wangzhen"對象。同上面的反編譯代碼

執(zhí)行到第二行代碼:
執(zhí)行到"wangzhen",判斷字符串常量池中是否存在"wangzhen",發(fā)現沒有,在字符串常量池中創(chuàng)建"wangzhen"字符串對象,然后把棧中的str6變量的引用指向"wangzhen"對象。

執(zhí)行到第三行代碼:
這里我們看到就是str5手動把"wangzhen"放在字符串常量池中,我們發(fā)現,在字符串常量池中已存在"wangzhen",于是str5 .intern()就是"wangzhen"對象的地址。我們還沒沒有收到返回值

如下圖,我們看到肯定返回false,此時str5.intern() == str6 (true)

6. 例子五

String str7 = new String("wang") + new String("zhen");
String str8 = "wangzhen";
System.out.println(str7.intern() == str8); // true
System.out.println(str7 == str8); // false
System.out.println(str8 == "wangzhen"); // true

執(zhí)行到第一行代碼:
同例子三和例子四的第一行代碼;

執(zhí)行到第二行代碼:
先判斷字符串常量池中是否存在"wangzhen"對象,發(fā)現沒有,我們在字符串常量池中創(chuàng)建"wangzhen"字符串對象;

執(zhí)行到第三行代碼:
執(zhí)行到str7.intern()這里,我們看到就是str7手動把"wangzhen"放在字符串常量池中,在字符串常量池中已結存在"wangzhen",于是把字符串常量池"wangzhen"的地址?,F在str7和str7 .intern()一樣

執(zhí)行到第四行代碼:
str7的指向為堆中的"wangzhen",而str8指向則為字符串常量池中的"wangzhen",故不相同,返回false。

執(zhí)行到第五行代碼:
str8指向則為字符串常量池中的"wangzhen",執(zhí)行"wangzhen",則把已存在的字符串常量池中"wangzhen"返回,故相同,返回true。

7. 例子六

String str9 = new String("wang") + new String("zhen");
System.out.println(str9.intern() == str9); // true
System.out.println(str9 == "wangzhen"); // true

執(zhí)行到第一行代碼:
同上

執(zhí)行到第二行代碼:
執(zhí)行到str9.intern()這里,我們看到就是str9手動把"wangzhen"放在字符串常量池中,在字符串常量池中沒有"wangzhen",于是把str3 .intern()引用指向堆中的"wangzhen"的地址?,F在str9和str9.intern()一樣

執(zhí)行到第三行代碼:
str9指向堆內存中的"wangzhen",執(zhí)行到"wangzhen"時,發(fā)現字符串常量池中已存在,直接返回str9指向的引用即可,故返回true。

四、總結

經過這么多例子,大家應該明白了吧,還是要自己跟著例子進行換一下jvm內存圖,這樣就理解記憶,也不會輕易忘記!

到此這篇關于JDK8中String的intern()方法詳細解讀的文章就介紹到這了,更多相關JDK8中String的intern()內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 簡單了解SpringMVC與Struts2的區(qū)別

    簡單了解SpringMVC與Struts2的區(qū)別

    這篇文章主要介紹了簡單了解SpringMVC與Struts2的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2019-11-11
  • Java中List列表去重有序和無序的6種方法

    Java中List列表去重有序和無序的6種方法

    在日常的業(yè)務開發(fā)中,會遇到List中的重復數據去除掉的場景,本文就來介紹一下Java中List列表去重有序和無序的6種方法,具有一定的參考價值,感興趣的可以了解一下
    2024-03-03
  • springboot實現多實例crontab搶占定時任務(實例代碼)

    springboot實現多實例crontab搶占定時任務(實例代碼)

    這篇文章主要介紹了springboot實現多實例crontab搶占定時任務,本文通過實例代碼給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-01-01
  • 通過Java實現zip文件與rar文件解壓縮的詳細步驟

    通過Java實現zip文件與rar文件解壓縮的詳細步驟

    這篇文章主要給大家介紹了如何通過?Java?來完成?zip?文件與?rar?文件的解壓縮,文中通過代碼示例講解的非常詳細,對大家的學習或工作有一定的幫助,需要的朋友可以參考下
    2024-07-07
  • java Swing JFrame框架類中setDefaultCloseOperation的參數含義與用法示例

    java Swing JFrame框架類中setDefaultCloseOperation的參數含義與用法示例

    這篇文章主要介紹了java Swing JFrame框架類中setDefaultCloseOperation的參數含義與用法,結合實例形式分析了Swing組件的JFrame框架類中setDefaultCloseOperation方法的簡單使用技巧,需要的朋友可以參考下
    2017-11-11
  • 圖文詳解Java中的序列化機制

    圖文詳解Java中的序列化機制

    java中的序列化可能大家像我一樣都停留在實現Serializable接口上,對于它里面的一些核心機制沒有深入了解過。本文將通過示例帶大家深入了解Java中的序列化機制,需要的可以參考一下
    2022-10-10
  • MyBatis動態(tài)SQL中的trim標簽的使用方法

    MyBatis動態(tài)SQL中的trim標簽的使用方法

    這篇文章主要介紹了MyBatis動態(tài)SQL中的trim標簽的使用方法,需要的朋友可以參考下
    2017-05-05
  • springmvc實現導出數據信息為excle表格示例代碼

    springmvc實現導出數據信息為excle表格示例代碼

    本篇文章主要介紹了springmvc實現導出數據信息為excle表格,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧。
    2017-01-01
  • java?LeetCode普通字符串模擬題解示例

    java?LeetCode普通字符串模擬題解示例

    這篇文章主要為大家介紹了java?LeetCode普通字符串模擬題解示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-02-02
  • 一篇文章弄懂Spring MVC的參數綁定

    一篇文章弄懂Spring MVC的參數綁定

    這篇文章主要給大家介紹了關于如何通過一篇文章弄懂Spring MVC的參數綁定,文中通過示例代碼以及圖文介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2019-02-02

最新評論