讀古今文學網 > 編寫高質量代碼:改善Java程序的151個建議 > 第4章 字符串 >

第4章 字符串

Although the world is full of suffering, it is full also of the overcoming of it.

雖然世界充滿了苦難,但總是能戰勝的。

——Hellen Keller(海倫·凱勒,美國作家)

在Class的班級裡,Object班主任問道,「大家知道誰是我們班最受歡迎的同學嗎?」

大家面面相覷,不解其意,既而交頭接耳,竊竊私語,猛然間,所有的目光都投向了String同學,時光彷彿戛然而止,String詫異地左顧右盼,然後羞紅了臉,慢慢地低下了頭,同時右手緩緩地舉起來,直至胳膊完全伸直,形成一個大大「1」字。

建議52:推薦使用String直接量賦值

一般對象都是通過new關鍵字生成的,但是String還有第二種生成方式,也就是我們經常使用的直接聲明方式,比如Str str="a",即是通過直接量"a"進行賦值的。對於String對像來說,這種方式是極力推薦的,但不建議使用new String("a")的方式賦值。為什麼呢?我們來看一段程序:


public class Client{

public static void main(Stringargs){

String str1="中國";

String str2="中國";

String str3=new String("中國");

String str4=str3.intern();

//兩個直接量是否相等

boolean b1=(str1==str2);

//直接量和對象是否相等

boolean b2=(str1==str3);

//經過intern處理後的對象與直接量是否相等

boolean b3=(str1==str4);

}

}


注意看上面的程序,我們使用「==」判斷的是兩個對象的引用地址是否相同,也就是判斷是否為同一個對象,打印的結果是true, false, true。即有兩個直接量是同一個對像(經過intern處理後的String與直接量是同一個對像),但直接通過new生成的對象卻與之不相等,原因何在?

原因是Java為了避免在一個系統中大量產生String對像(為什麼會大量產生?因為String字符串是程序中最經常使用的類型),於是就設計了一個字符串池(也有叫做字符串常量池,String Pool或String Constant Pool或String Literal Pool),在字符串池中所容納的都是String字符串對象,它的創建機制是這樣的:創建一個字符串時,首先檢查池中是否有字面值相等的字符串,如果有,則不再創建,直接返回池中該對象的引用,若沒有則創建之,然後放到池中,並返回新建對象的引用,這個池和我們平常所說的池概念非常相似。對於此例子來說,就是在創建第一個「中國」字符串時,先檢查字符串池中有沒有該對象,發現沒有,於是就創建了「中國」這個字符串並放到池中,待再創建str2字符串時,由於池中已經有了該字符串,於是就直接返回了該對象的引用,此時,str1和str2指向的是同一個地址,所以使用「==」來判斷那當然是相等的了。

那為什麼使用new String(「中國」)就不相等了呢?因為直接聲明一個String對象是不檢查字符串池的,也不會把對像放到池中,那當然「==」為false了。

那為什麼使用intern方法處理後就又相等了呢?因為intern會檢查當前的對象在對像池中是否有字面值相同的引用對象,如果有則返回池中對象,如果沒有則放置到對像池中,並返回當前對象。

可能有讀者要問了,對像放到池中會不會產生線程安全問題呀?好問題,不過Java已經考慮到了,String類是一個不可變(Immutable)對像其實有兩層意思:一是String類是final類,不可繼承,不可能產生一個String的子類;二是在String類提供的所有方法中,如果有String返回值,就會新建一個String對象,不對原對像進行修改,這也就保證了原對象是不可改變的。

還有讀者問了,放到池中,是不是要考慮垃圾回收問題呀?不用考慮了,雖然Java的每個對象都保存在堆內存中,但是字符串池非常特殊,它在編譯期已經決定了其存在JVM的常量池(Constant Pool),垃圾回收器是不會對它進行回收的。

通過上面的介紹,我們發現Java在字符串的創建方面確實提供了非常好的機制,利用對像池不僅可以提高效率,同時也減少了內存空間的佔用,建議大家在開發中使用直接量賦值方式,除非確有必要才新建立一個String對象。