我看到一個問題, 是關於 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.