閱讀原文:

面試別再問我String了?

mp.weixin.qq.com
圖標

因為我已爛熟於心,靈活運用了,別考了。哈哈,希望你能讀後思考一下這樣對面試官說。

String 簡介

String定義:

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}

為什麼設計為不可變類呢?

String設計為不可變類主要考慮到:效率和安全

  • 效率:

    1.在早期的JVM實現版本中,被final修飾的方法會被轉為內嵌調用以提升執行效率。而從Java SE5/6開始,就漸漸擯棄這種方式了。因此在現在的Java SE版本中,不需要考慮用final去提升方法調用效率。只有在確定不想讓該方法被覆蓋時,才將方法設置為final。

    2.緩存hashcode,String不可變,所以hashcode不變,這樣緩存才有意義,不必重新計算。
  • 安全:String常被作為網路連接,文件操作等參數類型,倘若可改變,會出現意想不到的結果。

測試掌握程度

為了不浪費你的時間,請看下面的題目,若你一目了然,可以跳過本文了。

public class Test {
public static void main(String[] args) {
String str1 = "HelloFlyapi";
String str2 = "HelloFlyapi";
String str3 = new String("HelloFlyapi");
String str4 = "Hello";
String str5 = "Flyapi";
String str6 = "Hello" + "Flyapi";
String str7 = str4 + str5;

System.out.println("str1 == str2 result: " + (str1 == str2));

System.out.println("str1 == str3 result: " + (str1 == str3));

System.out.println("str1 == str6 result: " + (str1 == str6));

System.out.println("str1 == str7 result: " + (str1 == str7));

System.out.println("str1 == str7.intern() result: " + (str1 == str7.intern()));

System.out.println("str3 == str3.intern() result: " + (str3 == str3.intern()));
}
}

String 的創建方式

從上面的題中你會知道,String的創建方式有兩種:

直接賦值

  • 此方式在方法區中字元串常量池中創建對象1、String str = "flyapi";

構造器

  • 此方式在堆內存創建對象1、String str = new String();

分析

要理解String,那麼要了解JVM內存中的棧(stack)、堆(heap)和方法區。簡要圖如下:

str1 == str2

String str1 = "HelloFlyapi";
String str2 = "HelloFlyapi";

System.out.println(str1 == str2); // true

當執行第一句時,JVM會先去常量池中查找是否存在HelloFlyapi,當存在時直接返回常量池裡的引用;當不存在時,會在字元創常量池中創建一個對象並返回引用。

當執行第二句時,同樣的道理,由於第一句已經在常量池中創建了,所以直接返回上句創建的對象的引用。

str1 == str3

String str1 = "HelloFlyapi";
String str3 = new String("HelloFlyapi");

System.out.println(str1 == str3); // false

執行第一句,同上第一句。

執行第二句時,會在堆(heap)中創建一個對象,當字元創常量池中沒有『HelloFlyapi』時,會在常量池中也創建一個對象;當常量池中已經存在了,就不會創建新的了。

str1 == str6

String str1 = "HelloFlyapi";
String str6 = "Hello" + "Flyapi";

System.out.println(str1 == str6); // true

由於」Hello」和」Flyapi」都是常量,編譯時,第二句會被自動編譯為『String str6 = 「HelloFlyapi」;

str1 == str7

String str1 = "HelloFlyapi";
String str4 = "Hello";
String str5 = "Flyapi";
String str7 = str4 + str5;

System.out.println(str1 == str7); // false

其中前三句變數存儲的是常量池中的引用地址。

第四句執行時,JVM會在堆(heap)中創建一個以str4為基礎的一個StringBuilder對象,然後調用StringBuilder的append()方法完成與str5的合併,之後會調用toString()方法在堆(heap)中創建一個String對象,並把這個String對象的引用賦給str7。

常用方法

下面是 String 類支持的方法,更多詳細,參看 Java String API 文檔:

String相關

由於String的不可變性導致,字元串變更時效率低下,在之後得JDK版本中出現了StringBuilder和StringBuffer 。

類可變性線程安全String不可變安全StringBuffer可變安全StringBuilder可變非安全

使用選擇

1、當有少量連接操作時,使用String

2、當單線程下有大量連接操作時,使用StringBuilder

3、當多線程下有大量連接操作時,使用StringBuffer

常見String面試題

String str = new String(「abc」)創建了多少個實例?

這個問題其實是不嚴謹的,但面試一般會遇到,所以我們要補充來說明。

類的載入和執行要分開來講:

創建了兩個

1、當載入類時,」abc」被創建並駐留在了字元創常量池中(如果先前載入中沒有創建駐留 過)。

2、當執行此句時,因為」abc」對應的String實例已經存在於字元串常量池中,所以JVM會將此實例複製到會在堆(heap)中並返回引用地址。

通過位元組碼我們可以看到:

源碼:String str = new String(「abc」)

位元組碼:

Code:
0: new #2 // class java/lang/String
3: dup
4: ldc #3 // String abc
6: invokespecial #4 // Method java/lang/String."<init>":(Ljava/lang/String;)
9: astore_1
10: return

執行時僅(#2)創建了一個對象。

關於這個面試題,可以看看一個超大牛的回答:rednaxelafx.iteye.com/b

更多技術文章盡在微信公眾號 : 碼上實戰

給大家整理了一些面試的文檔和視頻:

公眾號回復: 面試視頻


推薦閱讀:
相关文章