深入理解String和StringBuilder

前言#

我偶爾會在問答里面看一看誰有問題,如果我知道就回答一下。今天偶然看到一個問題,去掉提問的代碼部分,問題就是:

String對象的intern()方法得到的對象,為什么與String對象有時候不相等?

我看到這個問題,我也是懵逼了,今天就寫個筆記復習一下。

正文#

首先,這個String對象的intern()方法是干什么用的呢?看一下源碼的注釋:

/**
* 英文注釋好長,這里就簡單翻譯一下
* 返回一個這個String對象的權威代表(請注意,這里返回的是代表,沒說返回是自己)
* 有一個字符串池,專門用來維持String對象,當intern方法被調用的時候,返回和他equals方法相同的String對象,如果沒有,就把這個String添加到池中,再把這個String對象返回(也就是說這個情況,返回了自己)
* 
* s.intern() == t.intern(),只有在s.equals(t)等于true
*/     
public native String intern();

看來這個方法和equals關系密切,所以再看一下equals方法:

public boolean equals(Object anObject) {
    // 先判斷是否是同一個對象
        if (this == anObject) {
            return true;
        }
        // 判斷是否是String類型
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = count;
            // 判斷字符串的長度是否相等
            if (n == anotherString.count) {
                int i = 0;
                // 判斷每一個位置的字符是否相等
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

equals判斷的僅僅的是字符串的內容,所以只要內容相同,在字符串池中都不會重復添加。

結合我們已經對字符串的了解,我們可以總結出一下幾點:

1、字符串池中,只包含唯一內容的字符串。
2、字符串池,提供了相同字符串之間的復用機制,防止不同字符串創建多個對象。

這個時候突然想起來剛接觸Java時的一個面試題:

String s = "abc" 和 String s = new String("abc") 的區別

這個問題大對數都能答對:

String s = "abc" 是先使用字符串池中的abc對象,如果沒有創建abc并添加到字符串池中,這個邏輯和intern()方法是完全一樣的, 所以這種使用方法也是推薦的使用方法。

String s = new String("abc") ,一開始的過程和第一種是一樣的,同樣是先從字符串池中獲取,然后根據情況添加或者返回。但是new操作符,會返回一個新的String對象,也就是說,返回的String對象并不是abc。但是這種方法會出現內存的浪費,所以并不推薦使用。

為了驗證我們的想法,我們運行一個小demo:

public class Main {
    
    public static void main(String[] args){
        // 注意這里通過創建StringBuilder,已經創建了111,并加入到字符串池
        String s1 = new StringBuilder("111").toString();
    // 這里還是通過相同的方式,看看是否返回了跟s1相同的對象
        String s2 = new StringBuilder("111").toString();
        // 直接從字符串池中得到對象
        String s3 = "111";
        String s4 = "111";
       
        System.out.println(s1.equals(s2));
        System.out.println(s1 == s2);

        System.out.println(s3.equals(s4));
        System.out.println(s3 == s4);
    }

}

首先我們使用了兩個StringBuilder來拼接字符串,看看得到的結果,然后直接從字符串池中去取,看看得到是不是同一個對象。

true
false
true
true

第一個結果是s1.equals(s2) =true,這個沒有疑問,對比內容必然是相同的。
第二個結果是s1 == s2 得到false,說明是s1和s2是相同內容的不同對象。

為什么不是相同對象呢?看一下StringBuilder的toString()方法:

@Override
public String toString() {
        // Create a copy, don't share the array
   return new String(value, 0, count);
}

竟然是一個new String,怪不得對象是不相同的。

第三個結果s3.equals(s4) = true, 這個沒有疑問,對比內容必然是相同的。
第四個結果s3 == s4,一樣是true,說明得到確實是相同的對象。

總結#

有了剛才的驗證,我們基本上可以這么理解:

"abc" , 我們可以看做是單例模式,這個abc只創建一次,可以復用。
例如 String s = "abc", StringBuilder.append("abc"),實際上使用的都是同一個字符串對象。

并且我們知道了平時使用字符串的幾個小細節:

1、String的equals()方法判斷的內容相同,不是判斷是否是相同對象。
2、StringBuilder的toString()方法,會創建新的字符串對象并返回,這個還是有優化空間的。

我們把之前學到的內容又重新復習了一遍,還找到了StringBuilder性能可以優化的地方,這次復習的收獲還是非常驚喜的,最后貼出那個朋友提出的問題:

public static void main(String[] argv){
String a = new StringBuilder("aa").append("計算機").toString();
System.out.println(a.intern()==a);
String b = new StringBuilder().append("計算機").toString();
System.out.println(b.intern()==b);
String c = new String("dsd");
System.out.println(c.intern()==c);
}
為何結果是
true
false
false
而不是
false
false
fasle
?

這個問題你能夠幫他解答嗎?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 一、String 類 1、定義: 1、從概念上講,java字符串就是Unicode字符序列。每個用雙引號括起來的字...
    玉圣閱讀 1,597評論 0 1
  • Tip:筆者馬上畢業了,準備開始 Java 的進階學習計劃。于是打算先從 String 類的源碼分析入手,作為后面...
    石先閱讀 12,046評論 16 58
  • 糖尿病分為一型和二型,臨床上絕大多數糖尿病患者都屬于二型糖尿病,到今天為止,幾乎所有的醫學論著,醫生和營養師都都在...
    花語88閱讀 294評論 0 0
  • 笑與淚最后都會連帶著冰冷的身軀臥進沃土之中。在那個萬念俱灰的封閉逼仄的空間里,臭蟲不會鉆進我們的大腦,窺視我...
    取個帥氣的昵稱拔閱讀 197評論 0 0