?? 0118.htm
字號:
579頁中程序<br>
<br>
可以想象出它“可能”是如何工作的:字串"abc"可以有一個方法append(),它新建了一個字串,其中包含"abc"以及foo的內(nèi)容;這個新字串然后再創(chuàng)建另一個新字串,在其中添加"def";以此類推。<br>
這一設(shè)想是行得通的,但它要求創(chuàng)建大量字串對象。盡管最終的目的只是獲得包含了所有內(nèi)容的一個新字串,但中間卻要用到大量字串對象,而且要不斷地進行垃圾收集。我懷疑Java的設(shè)計者是否先試過種方法(這是軟件開發(fā)的一個教訓(xùn)——除非自己試試代碼,并讓某些東西運行起來,否則不可能真正了解系統(tǒng))。我還懷疑他們是否早就發(fā)現(xiàn)這樣做獲得的性能是不能接受的。<br>
解決的方法是象前面介紹的那樣制作一個可變的同志類。對字串來說,這個同志類叫作StringBuffer,編譯器可以自動創(chuàng)建一個StringBuffer,以便計算特定的表達式,特別是面向String對象應(yīng)用覆蓋過的運算符+和+=時。下面這個例子可以解決這個問題:<br>
<br>
580頁程序<br>
<br>
創(chuàng)建字串s時,編譯器做的工作大致等價于后面使用sb的代碼——創(chuàng)建一個StringBuffer,并用append()將新字符直接加入StringBuffer對象(而不是每次都產(chǎn)生新對象)。盡管這樣做更有效,但不值得每次都創(chuàng)建象"abc"和"def"這樣的引號字串,編譯器會把它們都轉(zhuǎn)換成String對象。所以盡管StringBuffer提供了更高的效率,但會產(chǎn)生比我們希望的多得多的對象。<br>
<br>
12.4.4 String和StringBuffer類<br>
這里總結(jié)一下同時適用于String和StringBuffer的方法,以便對它們相互間的溝通方式有一個印象。這些表格并未把每個單獨的方法都包括進去,而是包含了與本次討論有重要關(guān)系的方法。那些已被覆蓋的方法用單獨一行總結(jié)。<br>
首先總結(jié)String類的各種方法:<br>
<br>
方法 自變量,覆蓋 用途<br>
<br>
構(gòu)建器 已被覆蓋:默認,String,StringBuffer,char數(shù)組,byte數(shù)組 創(chuàng)建String對象<br>
length() 無 String中的字符數(shù)量<br>
charAt() int Index 位于String內(nèi)某個位置的char<br>
getChars(),getBytes
開始復(fù)制的起點和終點,要向其中復(fù)制內(nèi)容的數(shù)組,對目標(biāo)數(shù)組的一個索引
將char或byte復(fù)制到外部數(shù)組內(nèi)部<br>
toCharArray() 無 產(chǎn)生一個char[],其中包含了String內(nèi)部的字符<br>
equals(),equalsIgnoreCase() 用于對比的一個String
對兩個字串的內(nèi)容進行等價性檢查<br>
compareTo() 用于對比的一個String 結(jié)果為負、零或正,具體取決于String和自變量的字典順序。注意大寫和小寫不是相等的!<br>
regionMatches() 這個String以及其他String的位置偏移,以及要比較的區(qū)域長度。覆蓋加入了“忽略大小寫”的特性
一個布爾結(jié)果,指出要對比的區(qū)域是否相同<br>
startsWith() 可能以它開頭的String。覆蓋在自變量里加入了偏移
一個布爾結(jié)果,指出String是否以那個自變量開頭<br>
endsWith() 可能是這個String后綴的一個String
一個布爾結(jié)果,指出自變量是不是一個后綴<br>
indexOf(),lastIndexOf() 已覆蓋:char,char和起始索引,String,String和起始索引
若自變量未在這個String里找到,則返回-1;否則返回自變量開始處的位置索引。lastIndexOf()可從終點開始回溯搜索<br>
substring() 已覆蓋:起始索引,起始索引和結(jié)束索引 返回一個新的String對象,其中包含了指定的字符子集<br>
concat() 想連結(jié)的String 返回一個新String對象,其中包含了原始String的字符,并在后面加上由自變量提供的字符<br>
relpace() 要查找的老字符,要用它替換的新字符 返回一個新String對象,其中已完成了替換工作。若沒有找到相符的搜索項,就沿用老字串<br>
toLowerCase(),toUpperCase() 無 返回一個新String對象,其中所有字符的大小寫形式都進行了統(tǒng)一。若不必修改,則沿用老字串<br>
trim() 無 返回一個新的String對象,頭尾空白均已刪除。若毋需改動,則沿用老字串<br>
valueOf() 已覆蓋:object,char[],char[]和偏移以及計數(shù),boolean,char,int,long,float,double
返回一個String,其中包含自變量的一個字符表現(xiàn)形式<br>
Intern() 無 為每個獨一無二的字符順序都產(chǎn)生一個(而且只有一個)String句柄<br>
<br>
可以看到,一旦有必要改變原來的內(nèi)容,每個String方法都小心地返回了一個新的String對象。另外要注意的一個問題是,若內(nèi)容不需要改變,則方法只返回指向原來那個String的一個句柄。這樣做可以節(jié)省存儲空間和系統(tǒng)開銷。<br>
下面列出有關(guān)StringBuffer(字串緩沖)類的方法:<br>
<br>
方法 自變量,覆蓋 用途<br>
<br>
構(gòu)建器 已覆蓋:默認,要創(chuàng)建的緩沖區(qū)長度,要根據(jù)它創(chuàng)建的String
新建一個StringBuffer對象<br>
toString() 無 根據(jù)這個StringBuffer創(chuàng)建一個String<br>
length() 無 StringBuffer中的字符數(shù)量<br>
capacity() 無 返回目前分配的空間大小<br>
ensureCapacity() 用于表示希望容量的一個整數(shù) 使StringBuffer容納至少希望的空間大小<br>
setLength() 用于指示緩沖區(qū)內(nèi)字串新長度的一個整數(shù)
縮短或擴充前一個字符串。如果是擴充,則用null值填充空隙<br>
charAt() 表示目標(biāo)元素所在位置的一個整數(shù)
返回位于緩沖區(qū)指定位置處的char<br>
setCharAt() 代表目標(biāo)元素位置的一個整數(shù)以及元素的一個新char值
修改指定位置處的值<br>
getChars()
復(fù)制的起點和終點,要在其中復(fù)制的數(shù)組以及目標(biāo)數(shù)組的一個索引
將char復(fù)制到一個外部數(shù)組。和String不同,這里沒有g(shù)etBytes()可供使用<br>
append() 已覆蓋:Object,String,char[],特定偏移和長度的char[],boolean,char,int,long,float,double
將自變量轉(zhuǎn)換成一個字串,并將其追加到當(dāng)前緩沖區(qū)的末尾。若有必要,同時增大緩沖區(qū)的長度<br>
insert() 已覆蓋,第一個自變量代表開始插入的位置:Object,String,char[],boolean,char,int,long,float,double
第二個自變量轉(zhuǎn)換成一個字串,并插入當(dāng)前緩沖區(qū)。插入位置在偏移區(qū)域的起點處。若有必要,同時會增大緩沖區(qū)的長度<br>
reverse() 無 反轉(zhuǎn)緩沖內(nèi)的字符順序<br>
<br>
最常用的一個方法是append()。在計算包含了+和+=運算符的String表達式時,編譯器便會用到這個方法。insert()方法采用類似的形式。這兩個方法都能對緩沖區(qū)進行重要的操作,不需要另建新對象。<br>
<br>
12.4.5 字串的特殊性<br>
現(xiàn)在,大家已知道String類并非僅僅是Java提供的另一個類。String里含有大量特殊的類。通過編譯器和特殊的覆蓋或過載運算符+和+=,可將引號字符串轉(zhuǎn)換成一個String。在本章中,大家已見識了剩下的一種特殊情況:用同志StringBuffer精心構(gòu)造的“不可變”能力,以及編譯器中出現(xiàn)的一些有趣現(xiàn)象。<br>
<br>
12.5 總結(jié)<br>
由于Java中的所有東西都是句柄,而且由于每個對象都是在內(nèi)存堆中創(chuàng)建的——只有不再需要的時候,才會當(dāng)作垃圾收集掉,所以對象的操作方式發(fā)生了變化,特別是在傳遞和返回對象的時候。舉個例子來說,在C和C++中,如果想在一個方法里初始化一些存儲空間,可能需要請求用戶將那片存儲區(qū)域的地址傳遞進入方法。否則就必須考慮由誰負責(zé)清除那片區(qū)域。因此,這些方法的接口和對它們的理解就顯得要復(fù)雜一些。但在Java中,根本不必關(guān)心由誰負責(zé)清除,也不必關(guān)心在需要一個對象的時候它是否仍然存在。因為系統(tǒng)會為我們照料一切。我們的程序可在需要的時候創(chuàng)建一個對象。而且更進一步地,根本不必擔(dān)心那個對象的傳輸機制的細節(jié):只需簡單地傳遞句柄即可。有些時候,這種簡化非常有價值,但另一些時候卻顯得有些多余。<br>
可從兩個方面認識這一機制的缺點:<br>
(1)
肯定要為額外的內(nèi)存管理付出效率上的損失(盡管損失不大),而且對于運行所需的時間,總是存在一絲不確定的因素(因為在內(nèi)存不夠時,垃圾收集器可能會被強制采取行動)。對大多數(shù)應(yīng)用來說,優(yōu)點顯得比缺點重要,而且部分對時間要求非常苛刻的段落可以用native方法寫成(參見附錄A)。<br>
(2)
別名處理:有時會不慎獲得指向同一個對象的兩個句柄。只有在這兩個句柄都假定指向一個“明確”的對象時,才有可能產(chǎn)生問題。對這個問題,必須加以足夠的重視。而且應(yīng)該盡可能地“克隆”一個對象,以防止另一個句柄被不希望的改動影響。除此以外,可考慮創(chuàng)建“不可變”對象,使它的操作能返回同種類型或不同種類型的一個新對象,從而提高程序的執(zhí)行效率。但千萬不要改變原始對象,使對那個對象別名的其他任何方面都感覺不出變化。<br>
<br>
有些人認為Java的克隆是一個笨拙的家伙,所以他們實現(xiàn)了自己的克隆方案(注釋⑤),永遠杜絕調(diào)用Object.clone()方法,從而消除了實現(xiàn)Cloneable和捕獲CloneNotSupportException違例的需要。這一做法是合理的,而且由于clone()在Java標(biāo)準(zhǔn)庫中很少得以支持,所以這顯然也是一種“安全”的方法。只要不調(diào)用Object.clone(),就不必實現(xiàn)Cloneable或者捕獲違例,所以那看起來也是能夠接受的。<br>
<br>
⑤:Doug Lea特別重視這個問題,并把這個方法推薦給了我,他說只需為每個類都創(chuàng)建一個名為duplicate()的函數(shù)即可。<br>
<br>
Java中一個有趣的關(guān)鍵字是byvalue(按值),它屬于那些“保留但未實現(xiàn)”的關(guān)鍵字之一。在理解了別名和克隆問題以后,大家可以想象byvalue最終有一天會在Java中用于實現(xiàn)一種自動化的本地副本。這樣做可以解決更多復(fù)雜的克隆問題,并使這種情況下的編寫的代碼變得更加簡單和健壯。<br>
<br>
12.6 練習(xí)<br>
(1) 創(chuàng)建一個myString類,在其中包含了一個String對象,以便用在構(gòu)建器中用構(gòu)建器的自變量對其進行初始化。添加一個toString()方法以及一個concatenate()方法,令其將一個String對象追加到我們的內(nèi)部字串。在myString中實現(xiàn)clone()。創(chuàng)建兩個static方法,每個都取得一個myString
x句柄作為自己的自變量,并調(diào)用x.concatenate("test")。但在第二個方法中,請首先調(diào)用clone()。測試這兩個方法,觀察它們不同的結(jié)果。<br>
(2) 創(chuàng)建一個名為Battery(電池)的類,在其中包含一個int,用它表示電池的編號(采用獨一無二的標(biāo)識符的形式)。接下來,創(chuàng)建一個名為Toy的類,其中包含了一個Battery數(shù)組以及一個toString,用于打印出所有電池。為Toy寫一個clone()方法,令其自動關(guān)閉所有Battery對象。克隆Toy并打印出結(jié)果,完成對它的測試。<br>
(3) 修改CheckCloneable.java,使所有clone()方法都能捕獲CloneNotSupportException違例,而不是把它直接傳遞給調(diào)用者。<br>
(4) 修改Compete.java,為Thing2和Thing4類添加更多的成員對象,看看自己是否能判斷計時隨復(fù)雜性變化的規(guī)律——是一種簡單的線性關(guān)系,還是看起來更加復(fù)雜。<br>
(5) 從Snake.java開始,創(chuàng)建Snake的一個深層復(fù)制版本。</p>
</table>
<p align="center"><script src="../../2.js"></script></a>
</body>
</html>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -