關於 String.intern() 的思考

我看到一個問題, 是關於 String.intern() 的, 感受比較有意思, 因而本身也去探索了一下, 有了一些本身的看法, 因而在此記錄下來.java

咱們首先來看一個例子:編程

// 1
String str1 = new StringBuilder("ja").append("va").toString();
System.out.println(str1.intern() == str1);

// 2
String str2 = new StringBuffer("編").append("程").toString();
System.out.println(str2.intern() == str2);

// 3
String str3 = new StringBuffer("編").append("程").toString();
System.out.println(str3.intern() == str3);

這個例子會輸出什麼呢? 有些讀者朋友可能沒有想到, 其實上面的例子在不一樣的 JDK 版本中運行, 會有不一樣的結果的. 那麼接下來咱們來試一下吧:segmentfault

// 使用 JDK6 進行編譯運行:
false, false, false
// 使用 JDK7 進行編譯運行:
false, true, false

爲何結果會不一樣呢?
首先咱們來看一下第一部分:app

String str1 = new StringBuilder("ja").append("va").toString();
System.out.println(str1.intern() == str1);

這裏 JDK6 和 JDK7 都是打印的 false, 其緣由是 "java" 字符串常量比較特殊, 它是固定存在字符串常量池中的, 所以 "str1.intern()" 返回的就是字符串常量池中的對象的引用, 和堆上的 str1 就天然是不相等了.ui

接下來咱們來分析一下第二部分的代碼:code

// 2
String str2 = new StringBuffer("編").append("程").toString();
System.out.println(str2.intern() == str2);

在這裏 JDK6 和 JDK7 的輸出有了差別, 其具體緣由是 JDK6 和 JDK7 對 String.intern() 方法的實現的不一樣.
在 JDK6 及之前的 JDK 中:對象

intern() 方法會把首次遇到的字符串實例 **複製** 到永久代中, 而後返回永久代中的實例.

而對於 JDK7 以及之上的JDK:字符串

當遇到第一次出現的字符串時, intern() **再也不復制實例**, 而是在常量池中記錄首次出現的實例的引用, 而且 intern() 返回的是此實例引用.

根據 JDK6 和 JDK7 的 intern() 方法的區別, 咱們就知道了在第二部分的代碼中, "編程" 這個字符串是第一次出現的, 所以在 JDK6 中, 會將此對象的實例拷貝一份而後存放到常量池中, intern() 返回的是在常量池中拷貝後的新對象的引用, 進而就和堆上的 str1 不等了; 而在 JDK7 中, 因爲 intern() 方法的實現不一樣, 這個方法並不會拷貝一份對象實例到常量池中, 而是在常量池中記錄此對象的引用, 所以 intern() 返回的引用其實和堆中的 str1 是同樣的.get

有了前面的認知, 咱們對第三部分的代碼的輸出結果就比較熟悉了:編譯

// 3
String str3 = new StringBuffer("編").append("程").toString();
System.out.println(str3.intern() == str3);

第三部分代碼和第二部分代碼實際上是同樣的, "編程" 這個字符串已經在第二部分的代碼中出現了, 所以在常量池中已經存在了, 所以 str3.intern() 返回的是常量池中的對象的引用, 和堆上的 str3 天然是不同的, 所以不論在 JDK6 仍是 JDK7 中, 輸出的都是 false.

相關文章
相關標籤/搜索