面試別再問我String了
閱讀原文:
面試別再問我String了因為我已爛熟於心,靈活運用了,別考了。哈哈,希望你能讀後思考一下這樣對面試官說。
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)創建了一個對象。
關於這個面試題,可以看看一個超大牛的回答:http://rednaxelafx.iteye.com/blog/774673
更多技術文章盡在微信公眾號 : 碼上實戰
給大家整理了一些面試的文檔和視頻:
公眾號回復: 面試視頻
推薦閱讀: