前言#
我偶爾會在問答里面看一看誰有問題,如果我知道就回答一下。今天偶然看到一個問題,去掉提問的代碼部分,問題就是:
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
?
這個問題你能夠幫他解答嗎?