[005] - JavaSE面試題(五):String類
第一期:Java面試 - 100題,梳理各大網站優秀面試題。大家可以跟著我一起來刷刷Java理論知識
[005] -JavaSE面試題(五):String類
第1問:String、StringBuffer、StringBuilder 的區別?
- 都是final類,都不允許被繼承
- String長度是不可變的,StringBuffer和StringBuilder長度是可變的。
- StringBuffer是執行緒安全的,StringBuilder是執行緒不安全的,但他們兩個中的所有方法都是相同的,StringBuffer在StringBuilder的方法之上加了synchronized修飾,保證執行緒安全。
- StringBuilder比StringBuffer擁有更好的效能。
- 如果一個String型別的字串,在編譯時就可以確定是一個字串常量,則編譯完成之後,字串會自動拼接成一個常量。此時String的速度比StringBuilder和StringBuffer的效能更好。
第2問:String str="aaa" 與 String str=new String("aaa")一樣嗎?
- 不同,記憶體分配的方式不同。
- String str="aaa",建立了1個物件,建立的"aaa"是常量,jvm將其分配在常量池中;
- String str=new String("aaa"),建立了2個物件,一個是在常量池中,一個在堆記憶體中。
第3問:String str="aa" ,String s="bb" ,String aa=aa+s;
- 一共有2個引用,3個物件;
- "aa"與"bb"都是常量,常量的值不能改變,當執行字串拼接的時候,會建立一個新的常量"aabb",將其存到常量池中。
第4問:String s = "Hello";s = s + " world!";這兩行程式碼執行後,原始的 String 物件中的內容到底變了沒有?
沒有。
因為 String 被設計成不可變(immutable)類,所以它的所有物件都是不可變物件。在這段程式碼中,s 原先指向一個 String 物件,內容是 "Hello",然後我們對 s 進行了“+”操作,那麼 s 所指向的那個物件是沒有發生變化的。這時,s 不指向原來那個物件了,而指向了另一個 String 物件,內容為"Hello world!",原來那個物件還存在於記憶體之中,只是 s 這個引用變數不再指向它了。
結論,如果經常對字串進行各種各樣的修改,或者說,不可預見的修改,那麼使用 String 來代表字串的話會引起很大的記憶體開銷。因為 String 物件建立之後不能再改變,所以對於每一個不同的字串,都需要一個 String 物件來表示。這時,應該考慮使用 StringBuffer 類,它允許修改,而不是每個不同的字串都要生成一個新的物件。並且,這兩種類的物件轉換十分容易。同時,如果要使用內容相同的字串,不必每次都 new 一個 String。例如要在構造器中對一個名叫 s 的 String 引用變數進行初始化,把它設定為初始值,應當這樣做:
public class Demo { private String s; ... s = "Initial Value"; ... }
而非s = new String("Initial Value");
後者每次都會呼叫構造器,生成新物件,效能低下且記憶體開銷大,並且沒有意義,因為 String 物件不可改變,所以對於內容相同的字串,只要一個 String 物件來表示就可以了。也就說,多次呼叫上面的構造器建立多個物件,他們的 String 型別屬性 s 都指向同一個物件。
上面的結論還基於這樣一個事實:
- 對於字串常量,如果內容相同,Java 認為它們代表同一個 String 物件。
- 而用關鍵字 new 呼叫構造器,總是會建立一個新的物件,無論內容是否相同。
- 至於為什麼要把 String 類設計成不可變類,是它的用途決定的。其實不只 String,很多 Java 標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是面向物件思想的體現。
- 不可變類有一些優點,比如因為它的物件是隻讀的,所以多執行緒併發訪問也不會有任何問題。
- 當然也有一些缺點,比如每個不同的狀態都要一個物件來代表,可能會造成效能上的問題。所以 Java 標準類庫還提供了一個可變版本,即 StringBuffer。
第5問:判斷下面程式碼的執行結果
String s1 = new String("abc"); String s2 = "abc"; System.out.println(s1 == s2); //false System.out.println(s1.equals(s2)); //true
原因:s1記錄的是堆記憶體中物件的地址,s2記錄的是常量池中的地址
String s1 = "a" + "b" + "c"; String s2 = "abc"; System.out.println(s1 == s2); //true System.out.println(s1.equals(s2)); //true
原因:java中有常量優化機制,編譯時就把 "a" + "b" + "c"變成“abc”賦值給s1
String s1 = "ab"; String s2 = "abc"; String s3 = s1 + "c"; System.out.println(s3 == s2); //false System.out.println(s3.equals(s2)); //true
原因:因為s1是一個變數,jvm執行的時候不認為s3="abc",也就是無法使用常量池。因此s3會重新建立一個物件。