具體地說(shuō),當(dāng)你使用String b = b+"aa";這樣的語(yǔ)句的時(shí)候b實(shí)際上已經(jīng)不是以前的那個(gè)對(duì)象,,JVM重新劃分了一塊內(nèi)存保存b+"aa"的結(jié)果,,并使b的指向這塊新內(nèi)存。對(duì)于StringBuffer來(lái)說(shuō),,b.append("aa");執(zhí)行以后b仍然使用的是以前的那塊內(nèi)存空間,。同時(shí),對(duì)于原來(lái)b指向的內(nèi)存還有內(nèi)存回收的問(wèn)題,。
String類是不可變類,,任何對(duì)String的改變都會(huì)引發(fā)新的String對(duì)象的生成,;而StringBuffer則是可變類,任何對(duì)它所指代的字符串的改變都不會(huì)產(chǎn)生新的對(duì)象.當(dāng)改變字符串內(nèi)容時(shí),,采用StringBuffer能獲得更好的性能,。既然是為了獲得更好的性能,那么采用StringBuffer能夠獲得最好的性能嗎,?答案是NO,!為什么?如果你讀過(guò)《Think in Java》,,而且對(duì)里面描述HashTable和HashMap區(qū)別的那部分章節(jié)比較熟悉的話,,你一定也明白了原因所在。對(duì),,就是支持線程同步保證線程安全而導(dǎo)致性能下降的問(wèn)題,。HashTable是線程安全的,很多方法都是synchronized方法,,而HashMap不是線程安全的,,但其在單線程程序中的性能比HashTable要高。StringBuffer和StringBuilder類的區(qū)別也在于此,,新引入的StringBuilder類不是線程安全的,,但其在單線程中的性能比StringBuffer高。將 StringBuilder 的實(shí)例用于多個(gè)線程是不安全的,。如果需要這樣的同步,,則建議使用 StringBuffer 一個(gè)String對(duì)象的長(zhǎng)度是固定的,不能改變它的內(nèi)容,或者是附加新的字符至String對(duì)象中,。您也許會(huì)使用+來(lái)串聯(lián)字符串以達(dá)到附加新字符或字符串的目的,,但+會(huì)產(chǎn)生一個(gè)新的String實(shí)例。如果程序?qū)@種附加字符串的需求很頻繁,,并不建議使用+來(lái)進(jìn)行字符串的串聯(lián),。在面向?qū)ο蟪绦蛟O(shè)計(jì)中,最好是能重復(fù)運(yùn)用已生成的對(duì)象,,對(duì)象的生成需要內(nèi)存空間與時(shí)間,,不斷地產(chǎn)生String實(shí)例是一個(gè)沒(méi)有效率的行為。 J2SE 5.0提供java.lang.StringBuilder類,,使用這個(gè)類所產(chǎn)生的對(duì)象默認(rèn)會(huì)有16個(gè)字符的長(zhǎng)度,,您也可以自行指定初始長(zhǎng)度。如果附加的字符超出可容納的長(zhǎng)度,,則StringBuilder對(duì)象會(huì)自動(dòng)增加長(zhǎng)度以容納被附加的字符,。如果有頻繁作字符串附加的需求,使用StringBuilder會(huì)讓程序的效率大大提高,。通過(guò)下面的簡(jiǎn)單測(cè)試程序就可以知道效能差距有多大,。 ü 范例6.5 AppendStringTest.java public class AppendStringTest { public static void main(String[] args) { String text = ""; long beginTime = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) text = text + i; long endTime = System.currentTimeMillis(); System.out.println("執(zhí)行時(shí)間:" + (endTime - beginTime)); StringBuilder builder = new StringBuilder(""); beginTime = System.currentTimeMillis(); for(int i = 0; i < 10000; i++) builder.append(String.valueOf(i)); endTime = System.currentTimeMillis(); System.out.println("執(zhí)行時(shí)間:" + (endTime - beginTime)); } } 在范例6.5中首先使用+來(lái)串聯(lián)字符串,使用System.currentTimeMillis()取得for循環(huán)執(zhí)行前,、后的系統(tǒng)時(shí)間,,這樣就可以得知for循環(huán)執(zhí)行了多久。以下是我的計(jì)算機(jī)上的測(cè)試數(shù)據(jù): 執(zhí)行時(shí)間:4641 執(zhí)行時(shí)間:16 可以看到執(zhí)行的時(shí)間差距很大,,這說(shuō)明了使用+串聯(lián)字符串所帶來(lái)的負(fù)擔(dān),。如果有經(jīng)常作附加字符串的需求,建議使用StringBuilder,。事實(shí)上就范例6.5來(lái)說(shuō),,第二個(gè)for循環(huán)執(zhí)行時(shí)間還可以更短,因?yàn)閍ppend()也可以接受基本數(shù)據(jù)類型,,所以不必特地使用String.valueOf()方法從int取得String,。改為以下的方式,執(zhí)行時(shí)間可以大幅縮短: for(int i = 0; i < 10000; i++) builder.append(i); 使用StringBuilder最后若要輸出字符串結(jié)果,,可以用toString()方法,。可以使用length()方法得知目前對(duì)象中的字符長(zhǎng)度,,而capacity()可返回該對(duì)象目前可容納的字符容量,。另外,StringBuilder還有像insert()方法可以將字符插入指定的位置,,如果該位置以后有字符,,則將所有的字符往后移,;deleteChar()方法可以刪除指定位置的字符,而reserve()方法可以反轉(zhuǎn)字符串,。詳細(xì)的使用可以查詢java.lang.StringBuilder的API文件說(shuō)明,。 StringBuilder是J2SE 5.0才新增的類,在J2SE 5.0之前的版本若有相同的需求,,則使用java.lang.StringBuffer,。事實(shí)上,StringBuilder被設(shè)計(jì)為與StringBuffer具有相同的操作接口,。在單機(jī)非多線程(Multithread)的情況下使用StringBuilder會(huì)有較好的效率,,因?yàn)镾tringBuilder沒(méi)有處理同步(Synchronized)問(wèn)題。StringBuffer則會(huì)處理同步問(wèn)題,,如果StringBuilder會(huì)在多線程下被操作,,則要改用StringBuffer,讓對(duì)象自行管理同步問(wèn)題,。 String 字符串常量
StringBuffer 字符串變量(線程安全) StringBuilder 字符串變量(非線程安全) 簡(jiǎn)要的說(shuō), String 類型和 StringBuffer 類型的主要性能區(qū)別其實(shí)在于 String 是不可變的對(duì)象, 因此在每次對(duì) String 類型進(jìn)行改變的時(shí)候其實(shí)都等同于生成了一個(gè)新的 String 對(duì)象,,然后將指針指向新的 String 對(duì)象,,所以經(jīng)常改變內(nèi)容的字符串最好不要用 String ,因?yàn)槊看紊蓪?duì)象都會(huì)對(duì)系統(tǒng)性能產(chǎn)生影響,,特別當(dāng)內(nèi)存中無(wú)引用對(duì)象多了以后,, JVM 的 GC 就會(huì)開始工作,那速度是一定會(huì)相當(dāng)慢的,。 而如果是使用 StringBuffer 類則結(jié)果就不一樣了,,每次結(jié)果都會(huì)對(duì) StringBuffer 對(duì)象本身進(jìn)行操作,而不是生成新的對(duì)象,,再改變對(duì)象引用,。所以在一般情況下我們推薦使用 StringBuffer ,特別是字符串對(duì)象經(jīng)常改變的情況下,。而在某些特別情況下,, String 對(duì)象的字符串拼接其實(shí)是被 JVM 解釋成了 StringBuffer 對(duì)象的拼接,所以這些時(shí)候 String 對(duì)象的速度并不會(huì)比 StringBuffer 對(duì)象慢,,而特別是以下的字符串對(duì)象生成中,, String 效率是遠(yuǎn)要比 StringBuffer 快的: String S1 = “This is only a” + “ simple” + “ test”; StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple”).append(“ test”); 你會(huì)很驚訝的發(fā)現(xiàn),生成 String S1 對(duì)象的速度簡(jiǎn)直太快了,,而這個(gè)時(shí)候 StringBuffer 居然速度上根本一點(diǎn)都不占優(yōu)勢(shì),。其實(shí)這是 JVM 的一個(gè)把戲,在 JVM 眼里,,這個(gè) String S1 = “This is only a” + “ simple” + “test”; 其實(shí)就是: String S1 = “This is only a simple test”; 所以當(dāng)然不需要太多的時(shí)間了,。但大家這里要注意的是,,如果你的字符串是來(lái)自另外的 String 對(duì)象的話,速度就沒(méi)那么快了,,譬如: String S2 = “This is only a”; String S3 = “ simple”; String S4 = “ test”; String S1 = S2 +S3 + S4; 這時(shí)候 JVM 會(huì)規(guī)規(guī)矩矩的按照原來(lái)的方式去做
java.lang.StringBuilde 今天有空,,下了scjp的題目做做,,想起了一個(gè)本來(lái)想寫卻又沒(méi)寫的問(wèn)題。這個(gè)問(wèn)題幾乎已成為面試必問(wèn)題,,雖然答案很簡(jiǎn)單,,但回答出確又真正理解的人我覺(jué)得并不多,。 String與StringBuffer的區(qū)別? 如果你在網(wǎng)上一搜,,會(huì)有非常多的答案,,其中最多的就是String是不可變而StringBuffer是可變的,但是這可變與不可變究竟是什么意思呢,?如果你能用IDE進(jìn)行debug的話,,你就會(huì)發(fā)現(xiàn),String實(shí)例化以后所有的屬性都是final的,,而StringBuffer確不是,,這就是可變與不可變。下面引用SCJP的試題來(lái)解釋一下這個(gè)例子: java 代碼
答案是 javajavaC 這是因?yàn)榈谄咝衪ext = text.append ("C"),,append方法會(huì)改變text中的值,而text與textBuffer指向的地址是相同的,。因此會(huì)打印javaC 再舉個(gè)例子: String a = "a"; //假設(shè)a指向地址0x0001 a = "b";//重新賦值后a指向地址0x0002,但0x0001地址中保存的"a"依舊存在,,但已經(jīng)不再是a所指向的,。 因此String的操作都是改變賦值地址而不是改變值操作 |
|