http://www./artics/java/20071030365800.html
整理關(guān)于java的String類(lèi),equals函數(shù)和比較操作符的區(qū)別 |
作者:yourblog 來(lái)源:yourblog 整理日期:2007-10-30 |
初學(xué) Java 有段時(shí)間了,感覺(jué)似乎開(kāi)始入了門(mén),,有了點(diǎn)兒感覺(jué)但是發(fā)現(xiàn)很多困惑和疑問(wèn)而且均來(lái)自于最基礎(chǔ)的知識(shí)折騰了一陣子又查了查書(shū),,終于對(duì) String 這個(gè)特殊的對(duì)象有了點(diǎn)感悟大家先來(lái)看看一段奇怪的程序: public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; } } 這個(gè)程序真是簡(jiǎn)單啊,!可是有什么問(wèn)題呢,?
1. 來(lái)自 String 的憂慮上面這段程序中,到底有幾個(gè)對(duì)象呢,? 可能很多人脫口而出:兩個(gè),,s1 和 s2為什么?String 是 final 類(lèi),,它的值不可變,。看起來(lái)似乎很有道理,,那么來(lái)檢測(cè)一下吧,,稍微改動(dòng)一下程序就可以看到結(jié)果了: public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = "Monday"; if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); } } 呵呵,很多人都會(huì)說(shuō)已經(jīng)不止兩個(gè)對(duì)象了編譯并運(yùn)行程序,,輸出:s1 == s2?。? 為什么 s1 == s2 ? == 分明是在說(shuō):s1 與 s2 引用同一個(gè) String 對(duì)象 -- "Monday"!
2. 千變?nèi)f化的 String再稍微改動(dòng)一下程序,,會(huì)有更奇怪的發(fā)現(xiàn): public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2"); } } 我們將 s2 用 new 操作符創(chuàng)建程序輸出: s1 != s2s1 equals s2 嗯,,很明顯嘛s1 s2分別引用了兩個(gè)"Monday"String對(duì)象 可是為什么兩段程序不一樣呢?
3. 在 String 的游泳池中游泳哈哈,,翻了翻書(shū)終于找到了答案: 原來(lái),,程序在運(yùn)行的時(shí)候會(huì)創(chuàng)建一個(gè)字符串緩沖池當(dāng)使用 s2 = "Monday" 這樣的表達(dá)是創(chuàng)建字符串的時(shí)候,程序首先會(huì)在這個(gè)String緩沖池中尋找相同值的對(duì)象,,在第一個(gè)程序中,,s1先被放到了池中,所以在s2被創(chuàng)建的時(shí)候,,程序找到了具有相同值的 s1將 s2 引用 s1 所引用的對(duì)象"Monday"第二段程序中,,使用了 new 操作符,他明白的告訴程序:“我要一個(gè)新的,!不要舊的,!”與是一個(gè)新的"Monday"Sting對(duì)象被創(chuàng)建在內(nèi)存中。他們的值相同,,但是位置不同,,一個(gè)在池中游泳一個(gè)在岸邊休息。哎呀,真是資源浪費(fèi),,明明是一樣的非要分開(kāi)做什么呢,?
4. 繼續(xù)潛水再次更改程序: public class TestString { public static void main(String[] args) { String s1 = "Monday"; String s2 = new String("Monday"); s2 = s2.intern(); if (s1 == s2) System.out.println("s1 == s2"); else System.out.println("s1 != s2"); if (s1.equals(s2)) System.out.println("s1 equals s2"); else System.out.println("s1 not equals s2"); } } 這次加入:s2 = s2.intern(); 哇!程序輸出:s1 == s2 s1 equals s2原來(lái),,程序新建了 s2 之后,,又用intern()把他打翻在了池里哈哈,這次 s2 和 s1 有引用了同樣的對(duì)象了我們成功的減少了內(nèi)存的占用
5. == 與 equals() 的爭(zhēng)斗 String 是個(gè)對(duì)象,,要對(duì)比兩個(gè)不同的String對(duì)象的值是否相同明顯的要用到 equals() 這個(gè)方法可是如果程序里面有那么多的String對(duì)象,,有那么多次的要用到 equals ,哦,,天哪,,真慢啊更好的辦法:把所有的String都intern()到緩沖池去吧最好在用到new的時(shí)候就進(jìn)行這個(gè)操作String s2 = new String("Monday").intern();嗯,大家都在水池里泡著了嗎,?哈哈現(xiàn)在我可以無(wú)所顧忌的用 == 來(lái)比較 String 對(duì)象的值了真是爽啊,,又快又方便!
String 啊 String ,,讓 我說(shuō)你什么好呢,?你為 我們 Java 程序員帶來(lái)所有的困擾還不夠嗎? 看看 String 這一次又怎么鬧事兒吧 1. 回顧一下壞脾氣的 String 老弟 例程1:class Str { public static void main(String[] args) { String s = "Hi!"; String t = "Hi!"; if (s == t) System.out.println("equals"); else System.out.println("not equals"); } } 程序輸出什么呢,? 如果看客們看過(guò) 我的《來(lái)自 String 的困惑》之一相信你很快會(huì)做出正確的判斷:程序輸出:equals 2. 哦,,天哪,它又在攪混水了
例程2:class Str { public static void main(String[] args) { String s = "HELLO"; String t = s.toUpperCase(); if (s == t) System.out.println("equals"); else System.out.println("not equals"); } } 那么這個(gè)程序有輸出什么呢,? 慎重,!再慎重!不要被 String 這個(gè)迷亂的家伙所迷惑,! 它輸出:equalsWHY!!! 把程序簡(jiǎn)單的更改一下: class Str2 { public static void main(String[] args) { String s = "Hello"; String t = s.toUpperCase(); if (s == t) System.out.println("equals"); else System.out.println("not equals"); } } 你可能會(huì)說(shuō):不是一樣嗎,?不!千真萬(wàn)確的,,不一樣,!這一次輸出: not equalsOh MyGOD!!! 誰(shuí)來(lái)教訓(xùn)一下這個(gè) String 啊,!
3. 你了解你的馬嗎,? “要馴服脫韁的野馬,就要了解它的秉性”牛仔們說(shuō)道,。 你了解 String 嗎?解讀 String 的 API ,,可以看到:toUpperCase() 和 toLowerCase() 方法返回一個(gè)新的String對(duì)象,,它將原字符串表示字符串的大寫(xiě)或小寫(xiě)形勢(shì);但是要注意:如果原字符串本身就是大寫(xiě)形式或小寫(xiě)形式,那么返回原始對(duì)象,。這就是為什么第二個(gè)程序中 s 和 t 糾纏不清的緣故對(duì)待這個(gè)淘氣的,、屢教不改的 String ,似乎沒(méi)有更好的辦法了讓 我們解剖它,,看看它到底有什么結(jié)構(gòu)吧: (1) charAt(int n) 返回字符串內(nèi)n位置的字符,,第一個(gè)字符位置為0,最后一個(gè)字符的位置為length()-1,,訪問(wèn)錯(cuò)誤的位置會(huì)扔出一塊大磚頭:StringIndexOutOfBoundsException 真夠大的 (2) concat(String str) 在原對(duì)象之后連接一個(gè) str ,,但是返回一個(gè)新的 String 對(duì)象 (3) EqualsIgnoreCase(String str) 忽略大小寫(xiě)的 equals 方法這個(gè)方法的實(shí)質(zhì)是首先調(diào)用靜態(tài)字符方法toUpperCase() 或者 toLowerCase() 將對(duì)比的兩個(gè)字符轉(zhuǎn)換,然后進(jìn)行 == 運(yùn)算 (4) trim() 返回一個(gè)新的對(duì)象,,它將原對(duì)象的開(kāi)頭和結(jié)尾的空白字符切掉同樣的,,如果結(jié)果與原對(duì)象沒(méi)有差別,則返回原對(duì)象 (5) toString() String 類(lèi)也有 toString() 方法嗎,?真是一個(gè)有趣的問(wèn)題,,可是如果沒(méi)有它,你的 String 對(duì)象說(shuō)不定真的不能用在System.out.println() 里面啊小心,,它返回對(duì)象自己String 類(lèi)還有很多其他方法,,掌握他們會(huì)帶來(lái)很多方便也會(huì)有很多困惑,所以堅(jiān)持原則,,是最關(guān)鍵的
4. 我想買(mǎi)一匹更好的馬來(lái)購(gòu)買(mǎi)更馴服溫和的 String 的小弟 StringBuffer 吧 這時(shí)候會(huì)有人反對(duì): 它很好用,,它效率很高,它怎么能夠是小弟呢,? 很簡(jiǎn)單,,它的交互功能要比 String 少,如果你要編輯字符串它并不方便,,你會(huì)對(duì)它失望但這不意味著它不強(qiáng)大public final class String implements Serializable, Comparable, CharSequencepublic final class StringBuffer implements Serializable, CharSequence很明顯的,,小弟少了一些東東,不過(guò)這不會(huì)干擾它的前途StringBuffer 不是由 String 繼承來(lái)的不過(guò)要注意兄弟它也是 final 啊,,本是同根生看看他的方法吧,,這么多穩(wěn)定可靠的方法,用起來(lái)比頑皮的 String 要有效率的多? Java 為需要改變的字符串對(duì)象提供了獨(dú)立的 StringBuffer 類(lèi)它的實(shí)例不可變(final),,之所以要把他們分開(kāi)是因?yàn)?,字符串的修改要求系統(tǒng)的開(kāi)銷(xiāo)量增大,占用更多的空間也更復(fù)雜,,相信當(dāng)有10000人擠在一個(gè)狹小的游泳池里游泳而岸邊又有10000人等待進(jìn)入游泳池而焦急上火又有10000人在旁邊看熱鬧的時(shí)候,,你這個(gè) String 游泳池的管理員也會(huì)焦頭爛額在你無(wú)需改變字符串的情況下,簡(jiǎn)單的 String 類(lèi)就足夠你使喚的了,,而當(dāng)要頻繁的更改字符串的內(nèi)容的時(shí)候,,就要借助于宰相肚里能撐船的StringBuffer 了
5. 宰相肚里能撐船 (1) length() 與 capacity()String 中的 length() 返回字符串的長(zhǎng)度兄弟 StringBuffer 也是如此,,他們都由對(duì)象包含的字符長(zhǎng)度決定capacity()呢? public class TestCapacity { public static void main(String[] args){ StringBuffer buf = new StringBuffer("it was the age of wisdom,"); System.out.println("buf = " + buf); System.out.println("buf.length() = " + buf.length()); System.out.println("buf.capacity() = " + buf.capacity()); String str = buf.toString(); System.out.println("str = " + str); System.out.println("str.length() = " + str.length()); buf.append(" " + str.substring(0,18)).append("foolishness,"); System.out.println("buf = " + buf); System.out.println("buf.length() = " + buf.length()); System.out.println("buf.capacity() = " + buf.capacity()); System.out.println("str = " + str); } } 程序輸出: buf = it was the age of wisdom.buf.length() = 25 buf.capacity() = 41 str = it was the age of wisdomstr.length() = 25 buf = it was the age of wisdom, it was the age of foolishness, buf.length() = 56 buf.capacity() = 84 str = it was the age of wisdom,
可以看到,,在內(nèi)容更改之后,,capacity也隨之改變了長(zhǎng)度隨著向字符串添加字符而增加而容量只是在新的長(zhǎng)度超過(guò)了現(xiàn)在的容量之后才增加StringBuffer 的容量在操作系統(tǒng)需要的時(shí)候是自動(dòng)改變的程序員們對(duì)capacity所能夠做的僅僅是可以在初始化 StringBuffer對(duì)象的時(shí)候。
|
|