關於 Java String,這是面試的基礎,可是還有不少童鞋不能說清楚,因此本文將簡單而又透徹的說明一下那個讓你迷惑的 Stringjava
在 Java 中,咱們有兩種方式建立一個字符串面試
String x = "abc";
String y = new String("abc");複製代碼
你常見也常寫第一種,不多見第二種,但面試還總問這類問題,雙引號和構造器兩種形式建立字符串到底有什麼差異呢?緩存
先來看例子安全
String a = "abcd";
String b = "abcd";
System.out.println(a == b); // True
System.out.println(a.equals(b)); // True複製代碼
a == b
結果爲 true,是由於 a 和 b 都指向 方法區(method area) 同一個字符串文字,內存引用是同一個網絡
當屢次建立相同的字符串文字時,只存儲每一個不一樣字符串值的一個副本。這個叫作字符串留駐/留用,Java 中全部編譯期字符串常量都會被自動留駐函數
String c = new String("abcd");
String d = new String("abcd");
System.out.println(c == d); // False
System.out.println(c.equals(d)); // True複製代碼
c==d
結果爲 false,由於 c 和 d 的引用指向堆中不一樣的對象,不一樣的對象確定有不一樣的內存引用
工具
舉了兩個例子,文字描述有點懵?咱們來試圖經過圖形來理解上述兩種狀況:學習
也許你已經看看出來了,一個是在方法區
,一個是在堆
中,在 JVM 模型中這是兩個不一樣的區域,也許你面試時也常常被問到吧,來看下圖:ui
再次提醒一下,全部 new 的對象都會在 Heap 中,這樣之後你就好區分了spa
上面說的字符串留駐是在編譯期,那麼運行期能夠嗎?答案是確定的,咱們須要一個函數來幫忙
String c = new String("abcd").intern();
String d = new String("abcd").intern();
System.out.println(c == d); // Now true
System.out.println(c.equals(d)); // True複製代碼
看到 c == d
結果爲 true,你應該理解 intern (英文有拘留,軟禁的意思)的做用了,經過調用 intern()方法,就比如把建立的字符串拘留在方法區同樣了
在面試時甚至還會問你下面代碼建立了幾個對象:
String d = new String("abcd")複製代碼
因此,正常狀況下咱們不必使用構造器建立對象,由於這極可能會產生一個額外的沒用的對象,可是有例外哦,咱們下面說
String s = "abcd";
s = s.concat("ef");複製代碼
當咱們想在字符串 s 後面拼接字符"ef"時,會在堆中建立一個新的對象,並將 s 的引用指向新建立的對象,因爲 String 建立的是不可變對象,因此 String 類中的全部方法都不會改變它自身,而是返回一個新的字符串(快打開你的 IDE,看看是否每一個操做String 的方法最後都是返回有 return new String 字樣),到這裏你也應該理解了一個道理:
若是咱們須要一個字符串被修改,咱們最好使用 StringBuffer 或者 StringBuilder,不然,因爲每次操做字符串都會建立一個新的對象,而舊的對象不會有引用指向它,這樣咱們會浪費不少垃圾回收的時間
到這裏還沒完,你有沒有想過爲何 String 會被設置/制形成 final?
談及這個問題咱們須要一些倒推的或者相互約束思惟來思考
字符串池(String intern pool)是方法區域中的一個特殊存儲區域。當建立一個字符串時,若是該字符串已經存在於池中,那麼返回現有字符串的引用,而不是建立一個新對象。因此說,若是一個字符串是可變的,那麼改變一個引用的值,將致使本來指向該值的引用獲取到錯誤的值
字符串的hashcode在Java中常用。例如,在HashMap或HashSet中。不可變保證hashcode始終是相同的,這樣就能夠在不擔憂更改的狀況下兌現它。這意味着,不須要每次使用hashcode時都計算它。這樣更有效率。因此你會在 String 類中看到下面的成員變量的定義:
/** Cache the hash code for the string */
private int hash; // Default to 0複製代碼
String被普遍用做許多java類的參數,例如網絡鏈接、打開文件等。若是字符串不是不可變的,鏈接或文件將被更改,這可能致使嚴重的安全威脅。該方法認爲它鏈接到一臺機器上,但實際上並無。可變字符串也可能致使反射中的安全問題,由於參數是字符串。
因爲不可變對象不能被更改,因此它們能夠在多個線程之間自由共享。這消除了同步的需求。
總之,出於效率和安全性的考慮,String 被設計爲不可變的。這也是爲何在通常狀況下,不可變類是首選的緣由。
關於不可變對象和不可變引用老是有同窗搞不清楚
final User user = new User();複製代碼
上面的代碼指的是 user 引用不能被更改指向內存的其餘地址,可是因爲 User 是可變對象,咱們能夠調用 user 的 setter 方法修改其屬性
在String類中包含不少學問,包括你對JVM模型的理解,這也就是爲何面試官爲何喜歡問String,主要考察你的基本功
這是一款 IDEA 的主題插件,安裝後,選擇 Material Palenight
主題,同時做出以下設置
設置完後,你的 IDEA 就是下面這樣,引發極度溫馨
--------
歡迎持續關注公衆號:「日拱一兵」
- 前沿 Java 技術乾貨分享
- 高效工具彙總 | 回覆「工具」
- 面試問題分析與解答
- 技術資料領取 | 回覆「資料」
以讀偵探小說思惟輕鬆趣味學習 Java 技術棧相關知識,本着將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......