《你並不瞭解 String》 勘誤

寫技術文章其實是個很好的學習方式。首先你得自己摸清楚原理,然後才能嘗試去表達出來。你寫出來的東西,別人看了,給予你反饋,也是一個互相學習的過程。這不,前幾天碰到一個讀者提出的一個問題,讓我發現了自己文章中的一個疏漏,下面就來說說這個問題。

在我之前的一篇文章 你並不瞭解 String 中出了這樣一道題目:

String str1 = new String("j") + new String("ava"); // 1
str1.intern(); // 2
String str2 = "java"; // 3
System.out.println(str1 == str2); // 4

看一遍,想一遍,如果還不能準確給出答案的話可以再仔細看看我的那篇文章。正常情況下,答案應該是 true。貼一下之前的講解:

經過編譯,j 、ava 和 java 進入 Class 常量池 中。 類加載階段並不會創建實例,駐留字符串常量池。到運行期,第一行代碼中會創建 j 、ava 實例並駐留常量池,+ 會被 JVM 自動優化爲 StringBuilder ,拼接出 java 字符串,將 str1 指向該字符串實例。需要注意的是,這裏不會將 java 駐留到常量池。第二行代碼調用了 intern(),由於此時常量池中沒有 java,所以將 str1 的引用存入了常量池。第三行代碼,ldc 指令發現常量池中就有 java,直接返回常量池中其對應的引用,並賦給 str2。所以 str1 和 str2 是相等的。

看起來說的還是挺有道理的,我自己放到 IDEA 中執行也的確是 true。但是有一位讀者給我評論說他執行下來是 false。我的第一反應就是 “我的代碼肯定沒錯,應該是他寫錯了” ,就加了這兄弟的微信,截圖給我看了看代碼。然後我就蒙了,果然是 false。同樣的代碼,卻是不同的執行結果。

那麼,究竟 str1str2 爲什麼會不相等呢?

我們再來分析一下代碼,第一行:

String str1 = new String("j") + new String("ava"); // 1

從這句話中可以肯定的是,str1 指向堆中的一個 java 字符串實例,且這個字符串是在堆中新創建的。再看第二和第三行:

str1.intern(); // 2
String str2 = "java"; // 3

str1.intern()str1 是指向堆中的一個 java 字符串實例的,調用 intern() 的話,此時就有兩種情況:

  1. 字符串常量池中沒有 java 字符串的引用
  2. 字符串常量池中已經有了 java 字符串的引用

第一種情況,就是我上篇文章中的分析,str1 駐留到字符串常量池,結果是 true。第二種情況,字符串常量池中已經有了 java,此時再執行 str1.intern() 就會直接返回字符串常量池中 java 字符串對應的引用,並不會將 str1 駐留到字符串常量池。String str2 = "java"; 一執行,str2 等於字符串常量池中的 java 對應的引用。而 str1 是新建在堆中的 java 字符串的引用,自然而然,比較結果是 false

這麼分析下來,這位讀者無疑是第二種情況了。但是爲什麼同樣在 main() 方法中直接執行這幾行代碼,結果會不一樣呢?爲什麼 main() 方法中的代碼還沒有執行,字符串常量池中就已經有 java 字符串的引用了呢?

對啊,爲什麼會這樣呢?等等,java,這個字符串是不是有點特殊。我嘗試着讓這位讀者換一個特殊點的字符串再運行一次,竟然還讓我蒙對了,這下打印 true 了。看到這裏,你應該明白了,在 JVM 啓動的過程中,字符串常量池已經在發揮作用了,在 main() 方法運行之前,一些字符串引用已經駐留在字符串常量池,比如上面的 java,但也不是百分之百的,我手裏的 Ubuntu 18.04 就一直打印的是 true,你們也可以掏出電腦來試一試。

對於 String.intern() 方法,我們只需要搞清楚當前字符串常量池是否已經駐留該字符串引用,已駐留和未駐留將導致不同的執行邏輯。

  • 已駐留,直接返回字符串常量池中的引用
  • 未駐留,將當前字符串引用駐留進字符串常量池並返回該引用

這麼一來,我和這位讀者都徹徹底底的搞清楚了 intern() 方法,下次再遇到類似的面試題應該都不是問題了。所以呢,也歡迎大家多多提出自己的評論和想法,可以在掘金文章評論,可以微信搜索 秉心說 或者掃描文末二維碼關注公衆號私信我,也可以直接加我微信 bingxinshuo_ ,微信後臺回覆 秉心說 也可以加我微信。力所能及的問題我都會盡量解答。

文章越寫越短了,走進 JDK 系列好久不出新文章了,主要是最近輸出大於輸入,白天要上班,晚上還要奶孩子,每次文章大概都是十二點左右發出來的。不過好在最近的文章閱讀量都還不錯,也纔有了寫下去的動力。

大家也可以關注我的公衆號 "秉心說",這個名字的由來,其實很簡單,就是我女兒的名字。"秉心識本源,於事少凝滯",也是我的公衆號自動回覆的一句話,既是對孩子的期許,也是對自己的期許,也希望大家都可以做到 秉持內心,保持本源。(大白話翻譯一下,就是別忘了自己是個程序猿!)

說了一些題外話,後續還是會繼續保持輸入和輸出,專注 Java/Android 原創知識分享,不妨點個贊再走吧!

文章首發於微信公衆號: 秉心說 , 專注 Java 、 Android 原創知識分享,LeetCode 題解,歡迎關注!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章