1. 程式人生 > >基礎面試,為什麼面試官總喜歡問String?

基礎面試,為什麼面試官總喜歡問String?

關於 Java String,這是面試的基礎,但是還有很多童鞋不能說清楚,所以本文將簡單而又透徹的說明一下那個讓你迷惑的 String

在 Java 中,我們有兩種方式建立一個字串

String x = "abc";
String y = new String("abc");

你常見也常寫第一種,很少見第二種,但面試還總問這類問題,雙引號和構造器兩種形式建立字串到底有什麼差別呢?

先來看例子

例子 1

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 中所有編譯期字串常量都會被自動留駐

例子 2

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 模型中這是兩個不同的區域,也許你面試時也經常被問到吧,來看下圖:

再次提醒一下,所有 new 的物件都會在 Heap 中,這樣以後你就好區分了

執行期字串留駐

上面說的字串留駐是在編譯期,那麼執行期可以嗎?答案是肯定的,我們需要一個函式來幫忙

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")
  1. 如果方法區已存在"abcd", 那麼只建立一個 new String 的物件
  2. 如果方法區沒有"abcd", 那麼要建立兩個物件,一個在方法區,一個在堆中

所以,正常情況下我們沒必要使用構造器建立物件,因為這很可能會產生一個額外的沒用的物件,但是有例外哦,我們下面說

String s = "abcd";
s = s.concat("ef");

當我們想在字串 s 後面拼接字元"ef"時,會在堆中建立一個新的物件,並將 s 的引用指向新建立的物件,由於 String 建立的是不可變物件,所以 String 類中的所有方法都不會改變它自身,而是返回一個新的字串(快開啟你的 IDE,看看是否每個操作String 的方法最後都是返回有 return new String 字樣),到這裡你也應該理解了一個道理:

如果我們需要一個字串被修改,我們最好使用 StringBuffer 或者 StringBuilder,否則,由於每次操作字串都會建立一個新的物件,而舊的物件不會有引用指向它,這樣我們會浪費很多垃圾回收的時間

到這裡還沒完,你有沒有想過為什麼 String 會被設定/製造成 final?

為什麼 String 類被 final 修飾

談及這個問題我們需要一些倒推的或者相互約束思維來思考

字串池的需求

字串池(String intern pool)是方法區域中的一個特殊儲存區域。當建立一個字串時,如果該字串已經存在於池中,那麼返回現有字串的引用,而不是建立一個新物件。所以說,如果一個字串是可變的,那麼改變一個引用的值,將導致原本指向該值的引用獲取到錯誤的值

快取 hashcode

字串的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,主要考察你的基本功

靈魂追問

  1. String 和基本型別的包裝類如 Integer 和 Long 都被 final 修飾,但為什麼不建議作為 synchronized 同步塊的引數適用呢?
  2. 基本型別自動裝箱你知道發生了什麼嗎?和上一個問題有關係

提高效率工具

Material Theme UI

這是一款 IDEA 的主題外掛,安裝後,選擇 Material Palenight 主題,同時作出如下設定

設定完後,你的 IDEA 就是下面這樣,引起極度舒適


推薦閱讀

  • 每天用SpringBoot,還不懂RESTful API返回統一資料格式是怎麼實現的?
  • 雙親委派模型:大廠高頻面試題,輕鬆搞定
  • 面試還不知道BeanFactory和ApplicationContext的區別?
  • 如何設計好的RESTful API
  • 紅黑樹,超強動靜圖詳解,簡單易懂

歡迎持續關注公眾號:「日拱一兵」

  • 前沿 Java 技術乾貨分享
  • 高效工具彙總 | 回覆「工具」
  • 面試問題分析與解答
  • 技術資料領取 | 回覆「資料」

以讀偵探小說思維輕鬆趣味學習 Java 技術棧相關知識,本著將複雜問題簡單化,抽象問題具體化和圖形化原則逐步分解技術問題,技術持續更新,請持續關注......