本文轉載自【微信公眾號:java進階架構師,ID:java_jiagoushi】經微信公眾號授權轉載,如需轉載與原文作者聯繫
開篇介紹
大家好,我是Java面試題庫的提褲姐,本篇文章是面試系列文章的第五篇,主要介紹了JavaSE中String相關的面試題,在之後會順著開篇的思維導圖一直總結下去,做到日更!如果我能做到百日百更,希望你也可以跟著百日百刷,一百天養成一個好習慣。
Q:
String、StringBuffer、StringBuilder 的區別?
都是final類,都不允許被繼承String長度是不可變的,StringBuffer和StringBuilder長度是可變的。StringBuffer是線程安全的,StringBuilder是線程不安全的,但他們兩個中的所有方法都是相同的,StringBuffer在StringBuilder的方法之上加了synchronized修飾,保證線程安全。StringBuilder比StringBuffer擁有更好的性能。如果一個String類型的字符串,在編譯時就可以確定是一個字符串常量,則編譯完成之後,字符串會自動拼接成一個常量。此時String的速度比StringBuilder和StringBuffer的性能更好。
String str="aaa" 與 String str=new String("aaa")一樣嗎?
不同,內存分配的方式不同。
String str="aaa",創建了1個對象,創建的"aaa"是常量,jvm將其分配在常量池中;String str=new String("aaa"),創建了2個對象,一個是在常量池中,一個在堆內存中。
String str="aa" ,String s="bb" ,String aa=aa+s;
一共創建了幾個對象?
一共有2個引用,3個對象;
"aa"與"bb"都是常量,常量的值不能改變,當執行字符串拼接的時候,會創建一個新的常量"aabb",將其存到常量池中。
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 引用變量進行初始化,把它設置為初始值,應當這樣做:
1public class Demo {2 private String s;3 ...4 s = "Initial Value";5 ...6}
而非
s = new String("Initial Value");
後者每次都會調用構造器,生成新對象,性能低下且內存開銷大,並且沒有意義,因為 String 對象不可改變,所以對於內容相同的字符串,只要一個 String 對象來表示就可以了。也就說,多次調用上面的構造器創建多個對象,他們的 String 類型屬性 s 都指向同一個對象。上面的結論還基於這樣一個事實:對於字符串常量,如果內容相同,Java 認為它們代表同一個 String 對象。而用關鍵字 new 調用構造器,總是會創建一個新的對象,無論內容是否相同。至於為什麼要把 String 類設計成不可變類,是它的用途決定的。其實不只 String,很多 Java 標準類庫中的類都是不可變的。在開發一個系統的時候,我們有時候也需要設計不可變類,來傳遞一組相關的值,這也是面向對象思想的體現。不可變類有一些優點,比如因為它的對象是只讀的,所以多線程並發訪問也不會有任何問題。當然也有一些缺點,比如每個不同的狀態都要一個對象來代表,可能會造成性能上的問題。所以 Java 標準類庫還提供了一個可變版本,即 StringBuffer。
判斷下面代碼的執行結果
1String s1 = new String("abc"); 2String s2 = "abc";3System.out.println(s1 == s2); //false4System.out.println(s1.equals(s2)); //true
原因:s1記錄的是堆內存中對象的地址,s2記錄的是常量池中的地址
1String s1 = "a" + "b" + "c";2String s2 = "abc";3System.out.println(s1 == s2); //true 4System.out.println(s1.equals(s2)); //true
原因:java中有常量優化機制,編譯時就把 "a" + "b" + "c"變成「abc」賦值給s1
1String s1 = "ab";2String s2 = "abc";3String s3 = s1 + "c";4System.out.println(s3 == s2); //false5System.out.println(s3.equals(s2)); //true
原因:因為s1是一個變量,jvm運行的時候不認為s3="abc",也就是無法使用常量池。因此s3會重新創建一個對象。