條款6:避免創(chuàng)建不必要的對(duì)象(一)
在需要?個(gè)對(duì)象時(shí),恰當(dāng)?shù)淖龇ㄊ潜M可能重?這個(gè)對(duì)象??創(chuàng)建?個(gè)功能完全?樣的新對(duì)象。重?既快?時(shí)髦。如果對(duì)象是不可變的,那么它總是可以被重?(條款17)。
作為?個(gè)絕對(duì)不要這么做的極端示例,考慮如下語句:
String s = new String("bikini"); // 不要使用這種
該語句每次執(zhí)?時(shí)都會(huì)創(chuàng)建?個(gè)新的String實(shí)例,?這些對(duì)象的創(chuàng)建卻是完全沒必要的。String構(gòu)造?法的參數(shù)("bikini")本身就是個(gè)String實(shí)例,在功能上與由這個(gè)構(gòu)造?法所創(chuàng)建的對(duì)象完全?致。如果這種?法出現(xiàn)在循環(huán)中或是頻繁被調(diào)?的?法中,那就會(huì)創(chuàng)建出巨量毫?必要的String實(shí)例。
如下是改進(jìn)版本:
String s = “bikini";
該版本使?了單個(gè)String實(shí)例??每次在執(zhí)?時(shí)都創(chuàng)建?個(gè)新的實(shí)例。此外,它還確保了運(yùn)?在同?個(gè)虛擬機(jī)中并且包含了相同字符串字?值的其他代碼能夠重?該對(duì)象[JLS, 3.10.5]。
如果不變類既提供了靜態(tài)???法(條款1),也提供了構(gòu)造?法,那么你就可以通過前者來避免創(chuàng)建不必要的對(duì)象。?如說,???法Boolean.valueOf(String)要?構(gòu)造?法Boolean(String)更值得使?,后者已經(jīng)在Java 9中被標(biāo)記為不建議使?。構(gòu)造?法必須要在每次調(diào)?時(shí)創(chuàng)建新的對(duì)象,????法則沒有這個(gè)限制,在實(shí)踐中也不會(huì)這么做。除了重?不可變對(duì)象外,如果你知道對(duì)象不會(huì)被修改,那還可以重?可變對(duì)象。
?些對(duì)象的創(chuàng)建成本要?其他對(duì)象?昂很多。如果你不斷需要這種『昂貴的對(duì)象』,那么更好的?式就是將其緩存起來以重?。遺憾的是,在創(chuàng)建這樣的對(duì)象時(shí),這種情況并?那么顯?易?。假設(shè)你要編寫?個(gè)?法來確定?個(gè)字符串是否是個(gè)有效的羅?數(shù)字。下?是個(gè)最簡單的實(shí)現(xiàn)?式,它使?了正則表達(dá)式:
// 性能可以大大提高!
static boolean isRomanNumeral(String s) {
return s.matches("^(?=.)M*(C[MD]|D?C{0,3})");
}
上述實(shí)現(xiàn)的問題在于,它依賴于String.matches?法。雖然String.matches是檢查字符串是否匹配正則表達(dá)式最為簡單的?式,但它卻不適合于在性能關(guān)鍵的場景下重復(fù)使?。問題的原因是,它內(nèi)部會(huì)為正則表達(dá)式創(chuàng)建?個(gè)Pattern實(shí)例,但卻只使??次,接下來就會(huì)被垃圾回收掉。創(chuàng)建Pattern實(shí)例是?常昂貴的,因?yàn)樗枰獙⒄齽t表達(dá)式編譯為?個(gè)有限狀態(tài)機(jī)


為了改進(jìn)性能,請(qǐng)?jiān)陬惖某跏蓟^程中?動(dòng)將正則表達(dá)式編譯為Pattern實(shí)例(它是不可變的),然后將其緩存起來,并在每次調(diào)?isRomanNumeral?法時(shí)重?這個(gè)實(shí)例:
public class RomanNumerals {
private static final Pattern ROMAN = Pattern.compile(
"^(?=.)M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})$");
static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
}
改進(jìn)版本的isRomanNumeral在頻繁調(diào)?的情況下極?提升了性能。在我??的機(jī)器上,原始版本對(duì)于8字符的字符串花費(fèi)了1.1 μs,?改進(jìn)版本則只花費(fèi)了0.17 μs,??快了6.5倍。除了性能改進(jìn)外,代碼也更加清晰了(當(dāng)然,這?點(diǎn)是?仁?智的)。相?于不可?的Pattern實(shí)例,我們?yōu)槠渲付?個(gè)static final字段,這可以讓我們給它起個(gè)名字,這?點(diǎn)相?于正則表達(dá)式本身來說,可讀性更好了。