String-面試常考問題剖析

1. 爲什麼 String 類型要用 final 修飾?

爲啥這樣設計呢?, 請帶着這一個疑問看下去,從 String 類的源碼我們可以看出 String 是被 final 修飾的不可繼承類,源碼如下:
在這裏插入圖片描述

Java 語言之父 James Gosling 的回答是:

他會更傾向於使用 final,因爲它能夠緩存結果,當你在傳參時不需要考慮誰會修改它的值;如果是可變類的話,則有可能需要重新拷貝出來一個新值進行傳參,這樣在性能上就會有一定的損失。

那麼從 James Gosling的回答,可以知道他從兩個方面來考慮設計String類型爲 “final 的”

1.安全

當你在調用其他方法時,比如調用一些系統級操作指令之前,可能會有一系列校驗,如果是可變類的話,可能在你校驗過後,它的內部的值又被改變了,這樣有可能會引起嚴重的系統崩潰問題,這是迫使String類設計成不可變類的一個重要原因。

2.高效

以 JVM 中的字符串常量池來舉例,如下兩個變量:

String str1 = "java";
String str2 = "java";

只有字符串是不可變時,我們才能實現字符串常量池,字符串常量池可以爲我們緩存字符串,提高程序的運行效率,如下圖所示:
在這裏插入圖片描述
試想一下如果 String 是可變的,那當 s1 的值修改之後,s2 的值也跟着改變了,這樣就和我們預期的結果不相符了,因此也就沒有辦法實現字符串常量池的功能了。

2. == 和 equals 的區別是什麼?

比較官方的回答是這樣的:

== 對於基本數據類型來說,是用於比較 “值”是否相等的;而對於引用類型來說,是用於比較引用地址是否相同的。

查看源碼我們可以知道 Object 中也有 equals() 方法,源碼如下:

public boolean equals(Object obj) {
    return (this == obj);
}

可以看出,Object 中的 equals() 方法其實就是 ==,而 String 重寫了 equals() 方法把它修改成比較兩個字符串的值是否相等

  public boolean equals(Object anObject) {
  		//先判斷當前引用地址是否相同
        if (this == anObject) {
            return true;
        }
        //判斷類型是否爲String
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            //判斷長度是否一致 ,再進行循環比對字符是否相等
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

3. String 和 StringBuilder、StringBuffer 有什麼區別?

因爲String類型是不可變的,所以在字符串拼接的時候如果使用String的話性能會很低,因此我們就需要使用另一個數據類型StringBuffer,它提供了append和insert方法可用於字符串的拼接,它使用 synchronized 來保證線程安全,如下源碼所示:

在這裏插入圖片描述
因爲它使用了synchronized來保證線程安全,所以性能不是很高,於是在JDK1.5就有了StringBuilder,它同樣提供了append和insert的拼接方法,但它沒有…

4. String 的 intern() 方法有什麼含義?

String常見的創建方式有兩種,直接賦值的方式“Strings1=“Java”;”和“Strings2=newString(“Java”);”的方式,但兩者在JVM的存儲區域卻截然在JDK1.8中,變量s1會先去字符串常量池中找字符串“Java”,如果有相同的字符則直接返回常量句柄,如果沒有此字符串則會先在常量池中創建此字符串,然後再返回常量句柄;而變量s2是直接在堆上創建一個變量,如果調用 intern 方法纔會把此字符串保存到常量池中,如下代碼所示:

在這裏插入圖片描述

5.String 類型在 JVM(Java 虛擬機)中是如何存儲的?編譯器對 String 做了哪些優化?

JDK 1.7 之後把永生代換成的元空間,把字符串常量池從方法區移到了 Java 堆上。

除此之外編譯器還會對 String 字符串做一些優化,例如以下代碼:

在這裏插入圖片描述

雖然 s1 拼接了多個字符串,但對比的結果卻是 true,我們查看Class 文件可以知道JDK優化了

String s1 = "Java";
String s2 = "Java";
//結果爲true
System.out.println(s1 == s2);

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章