面試String的特點及重要方法(二)

上期呢我們對String的基本構造方法,源碼進行了簡單的總結,這期我們看看它比較更有魅力的地方,廢話不多,直接上:
飛上期

一,==和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;
    }
    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;
}

二,final修飾的好處

從String的源碼我們可已看出,String是被final修飾的不可繼承類,源碼如下:

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

那麼問題來了,爲什麼要這樣設計呢?

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

這是James Gosling的回答,他還說,迫使String類設計成不可變的原因是爲了安全,當你在調用其他方法時,比如調用一些系統操作指令前,可能會有一系列二的校驗,如果是可變類的話,可能在你校驗之後,他內部的值改變了,這樣有肯能引起系統崩潰,這也是final修飾String的重要原因之一。

總結來說,使用final修飾第一是爲了安全,第二是爲了高效。以JVM中的字符串常量池來說,如下兩個變量:

String s1 = "java";
String s2 = "java";

只有字符串不可變時,我們才能實現字符串常量池,他可以爲我們緩存字符串,提高運行效率,上圖:
在這裏插入圖片描述

試想一下,如果String是可變的,那當S1的值修改後,s2的值也變了,這樣就和我們預期的結果不相符了,因此也就沒辦法實現字符串常量池的功能了。

三,String,StringBuilder,Stringbuff的區別

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

@Override
public synchronized StringBuffer append(Object obj) {
    toStringCache = null;
    super.append(String.valueOf(obj));
    return this;
}

@Override
public synchronized StringBuffer append(String str) {
    toStringCache = null;
    super.append(str);
    return this;
}

因爲他使用了synchronized來保證線程安全,所以性能不用很高,於是在JDK1.5之後又有了StringBuilder,他同樣提供append和insert方法,但是他沒有使用synchronized修飾,因此在性能上要優於StringBuffer,所以在非併發操作的環境下可以使用StringBuilder來進行字符拼接。

四,String和JVM

String常見的創建方式有兩種:

String s1 = "Java";//第一種
String s2 = new String("Java");//第二種

但是兩者在JVM的存儲區域截然不同,在JDK1.8中,變量s1會先去字符串常量池中找字符串“Java”,如果有,則直接返回常量句柄,如果沒有,則會現在常量池中創建常量,然後再返回常量句柄,而s2直接實在堆上創建一個變量,如果調用intern方法纔會把字符串存在常量池中,如下代碼:

String s1 = new String("Java");
String s2 = s1.intern();
String s3 = "Java";
System.out.println(s1 == s2);//false
System.out.println(s2 == s3);//true

他們在JVM存儲位置如下:
在這裏插入圖片描述

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

String s1 = "Ja"+"va";
String s2 = "Java";
System.out.println(s1 == s2);

雖然s1拼接多個字符串,但是比較結果卻爲true,我們使用反編譯工具看到如下:
在這裏插入圖片描述
從#2可以看出,代碼"Ja"+"va"被直接編譯成Java,因此,s1==s2的結果才爲true,這就是編譯器對字符串優化的結果。

本期對Sring的總結就到這裏,有不足的地方可以指出。謝謝關注!

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