前言#
我偶爾會(huì)在問(wèn)答里面看一看誰(shuí)有問(wèn)題社搅,如果我知道就回答一下鸦难。今天偶然看到一個(gè)問(wèn)題标锄,去掉提問(wèn)的代碼部分,問(wèn)題就是:
String對(duì)象的intern()方法得到的對(duì)象譬涡,為什么與String對(duì)象有時(shí)候不相等闪幽?
我看到這個(gè)問(wèn)題,我也是懵逼了涡匀,今天就寫(xiě)個(gè)筆記復(fù)習(xí)一下沟使。
正文#
首先,這個(gè)String對(duì)象的intern()方法是干什么用的呢渊跋?看一下源碼的注釋?zhuān)?/p>
/**
* 英文注釋好長(zhǎng)腊嗡,這里就簡(jiǎn)單翻譯一下
* 返回一個(gè)這個(gè)String對(duì)象的權(quán)威代表(請(qǐng)注意着倾,這里返回的是代表,沒(méi)說(shuō)返回是自己)
* 有一個(gè)字符串池燕少,專(zhuān)門(mén)用來(lái)維持String對(duì)象卡者,當(dāng)intern方法被調(diào)用的時(shí)候,返回和他equals方法相同的String對(duì)象客们,如果沒(méi)有崇决,就把這個(gè)String添加到池中,再把這個(gè)String對(duì)象返回(也就是說(shuō)這個(gè)情況底挫,返回了自己)
*
* s.intern() == t.intern()恒傻,只有在s.equals(t)等于true
*/
public native String intern();
看來(lái)這個(gè)方法和equals關(guān)系密切,所以再看一下equals方法:
public boolean equals(Object anObject) {
// 先判斷是否是同一個(gè)對(duì)象
if (this == anObject) {
return true;
}
// 判斷是否是String類(lèi)型
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = count;
// 判斷字符串的長(zhǎng)度是否相等
if (n == anotherString.count) {
int i = 0;
// 判斷每一個(gè)位置的字符是否相等
while (n-- != 0) {
if (charAt(i) != anotherString.charAt(i))
return false;
i++;
}
return true;
}
}
return false;
}
equals判斷的僅僅的是字符串的內(nèi)容建邓,所以只要內(nèi)容相同盈厘,在字符串池中都不會(huì)重復(fù)添加。
結(jié)合我們已經(jīng)對(duì)字符串的了解官边,我們可以總結(jié)出一下幾點(diǎn):
1沸手、字符串池中,只包含唯一內(nèi)容的字符串注簿。
2契吉、字符串池,提供了相同字符串之間的復(fù)用機(jī)制诡渴,防止不同字符串創(chuàng)建多個(gè)對(duì)象捐晶。
這個(gè)時(shí)候突然想起來(lái)剛接觸Java時(shí)的一個(gè)面試題:
String s = "abc" 和 String s = new String("abc") 的區(qū)別
這個(gè)問(wèn)題大對(duì)數(shù)都能答對(duì):
String s = "abc" 是先使用字符串池中的abc對(duì)象,如果沒(méi)有創(chuàng)建abc并添加到字符串池中妄辩,這個(gè)邏輯和intern()方法是完全一樣的惑灵, 所以這種使用方法也是推薦的使用方法。
String s = new String("abc") 恩袱,一開(kāi)始的過(guò)程和第一種是一樣的泣棋,同樣是先從字符串池中獲取胶哲,然后根據(jù)情況添加或者返回畔塔。但是new操作符,會(huì)返回一個(gè)新的String對(duì)象鸯屿,也就是說(shuō)澈吨,返回的String對(duì)象并不是abc。但是這種方法會(huì)出現(xiàn)內(nèi)存的浪費(fèi)寄摆,所以并不推薦使用谅辣。
為了驗(yàn)證我們的想法,我們運(yùn)行一個(gè)小demo:
public class Main {
public static void main(String[] args){
// 注意這里通過(guò)創(chuàng)建StringBuilder婶恼,已經(jīng)創(chuàng)建了111桑阶,并加入到字符串池
String s1 = new StringBuilder("111").toString();
// 這里還是通過(guò)相同的方式柏副,看看是否返回了跟s1相同的對(duì)象
String s2 = new StringBuilder("111").toString();
// 直接從字符串池中得到對(duì)象
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);
}
}
首先我們使用了兩個(gè)StringBuilder來(lái)拼接字符串,看看得到的結(jié)果蚣录,然后直接從字符串池中去取割择,看看得到是不是同一個(gè)對(duì)象。
true
false
true
true
第一個(gè)結(jié)果是s1.equals(s2) =true萎河,這個(gè)沒(méi)有疑問(wèn)荔泳,對(duì)比內(nèi)容必然是相同的。
第二個(gè)結(jié)果是s1 == s2 得到false虐杯,說(shuō)明是s1和s2是相同內(nèi)容的不同對(duì)象玛歌。
為什么不是相同對(duì)象呢?看一下StringBuilder的toString()方法:
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
竟然是一個(gè)new String擎椰,怪不得對(duì)象是不相同的支子。
第三個(gè)結(jié)果s3.equals(s4) = true, 這個(gè)沒(méi)有疑問(wèn)确憨,對(duì)比內(nèi)容必然是相同的译荞。
第四個(gè)結(jié)果s3 == s4,一樣是true休弃,說(shuō)明得到確實(shí)是相同的對(duì)象吞歼。
總結(jié)#
有了剛才的驗(yàn)證,我們基本上可以這么理解:
"abc" , 我們可以看做是單例模式塔猾,這個(gè)abc只創(chuàng)建一次篙骡,可以復(fù)用。
例如 String s = "abc", StringBuilder.append("abc")丈甸,實(shí)際上使用的都是同一個(gè)字符串對(duì)象糯俗。
并且我們知道了平時(shí)使用字符串的幾個(gè)小細(xì)節(jié):
1、String的equals()方法判斷的內(nèi)容相同睦擂,不是判斷是否是相同對(duì)象得湘。
2、StringBuilder的toString()方法顿仇,會(huì)創(chuàng)建新的字符串對(duì)象并返回淘正,這個(gè)還是有優(yōu)化空間的。
我們把之前學(xué)到的內(nèi)容又重新復(fù)習(xí)了一遍臼闻,還找到了StringBuilder性能可以優(yōu)化的地方鸿吆,這次復(fù)習(xí)的收獲還是非常驚喜的,最后貼出那個(gè)朋友提出的問(wèn)題:
public static void main(String[] argv){
String a = new StringBuilder("aa").append("計(jì)算機(jī)").toString();
System.out.println(a.intern()==a);
String b = new StringBuilder().append("計(jì)算機(jī)").toString();
System.out.println(b.intern()==b);
String c = new String("dsd");
System.out.println(c.intern()==c);
}
為何結(jié)果是
true
false
false
而不是
false
false
fasle
?
這個(gè)問(wèn)題你能夠幫他解答嗎述呐?