Effective Java Note (對(duì)象的創(chuàng)建和銷(xiāo)毀)
一雹姊、對(duì)象的創(chuàng)建和銷(xiāo)毀
1. 考慮使用靜態(tài)工廠方法替代構(gòu)造器
優(yōu)點(diǎn)
- 靜態(tài)工廠方法可以有一個(gè)名稱(chēng),易于理解與閱讀渠缕,工廠方法的名稱(chēng)可以凸顯出不同構(gòu)造器的區(qū)別
- 每次掉用的時(shí)候可以不用都創(chuàng)建一個(gè)新的對(duì)象仰楚,而是可以選擇復(fù)用對(duì)象,在一些情況就可以直接使用==判斷相等薛躬,而不是equals
- 可以返回原返回類(lèi)型的任意子類(lèi)型俯渤。可以參考java的集合框架型宝。使用接口來(lái)引用被返回的對(duì)象八匠,而不是通過(guò)實(shí)現(xiàn)類(lèi)來(lái)返回對(duì)象引用是一個(gè)好習(xí)慣。
- 在創(chuàng)建參數(shù)化類(lèi)型實(shí)例的時(shí)候代碼更簡(jiǎn)潔诡曙。例如通過(guò)類(lèi)型推倒與泛型:
public static <K,V> HashMap<K,V> newInstance(){
return new HashMap<K,V>();
}
Map<String,List<String>> m = HashMap.newInstance();
缺點(diǎn)
- 如果類(lèi)不含有共有或者受保護(hù)的構(gòu)造器臀叙,那么將不能被子類(lèi)化。(可以通過(guò)組合化解)
- 與其他的靜態(tài)方法沒(méi)有實(shí)質(zhì)差別价卤。一個(gè)好的命名這時(shí)候就很重要了劝萤,讓人一看就知道是一個(gè)靜態(tài)方法的作用。
靜態(tài)工廠方法和構(gòu)建器對(duì)于有多個(gè)可選參數(shù)的時(shí)候慎璧,將增加復(fù)雜度與降低可讀性
2. 遇到多個(gè)構(gòu)造器參數(shù)時(shí)考慮使用構(gòu)建器
重疊構(gòu)造器在少數(shù)參數(shù)的時(shí)候可以床嫌,可以方便按需選擇構(gòu)造器,但是參數(shù)很多的時(shí)候胸私,很容易造成混亂厌处,閱讀型也會(huì)大大下降。有時(shí)候即使參數(shù)類(lèi)型是對(duì)的岁疼,但是最終結(jié)果也可能時(shí)錯(cuò)誤的阔涉。
此外基于JavaBeans思想可以使用 setXXX,為每個(gè)參數(shù)設(shè)置setter方法捷绒,這樣每個(gè)構(gòu)造參數(shù)都能很容易理解其作用瑰排,但是一個(gè)很?chē)?yán)重的缺點(diǎn)是:構(gòu)造的過(guò)程被分配到了幾個(gè)調(diào)用過(guò)程,容易造成不一致?tīng)顟B(tài)(尤其時(shí)多線(xiàn)程環(huán)境下)暖侨,而要保證一致?tīng)顟B(tài)需要付出很多精力去維護(hù)椭住。
構(gòu)建器一般是一個(gè)需要實(shí)例化的類(lèi)的靜態(tài)內(nèi)部類(lèi),提供了對(duì)參數(shù)的默認(rèn)設(shè)置字逗,同時(shí)對(duì)外可以提供鏈?zhǔn)秸{(diào)用進(jìn)行構(gòu)造京郑。例如:
Car car = new Car.Builder().name("benz").wheels(4).color(Color.BLACK).build();
而且在Builder的每個(gè)域中都可以進(jìn)行約束判斷,違反約束條件的情況可以跑出非法異常葫掉。Builder很好的結(jié)合了setter和重疊構(gòu)造器的優(yōu)點(diǎn)些举,參數(shù)數(shù)量可以更靈活,閱讀性更好
還可以考慮使用抽象工廠:
public interface Builder<T>{
T build();
}
缺點(diǎn)
為了構(gòu)造一個(gè)實(shí)例需要多生成一個(gè)實(shí)例(Builder類(lèi))挖息。
3. 使用私有構(gòu)造器或者枚舉類(lèi)型強(qiáng)化單例(Singleton)
- 一個(gè)類(lèi)中之提供了一個(gè)私有的無(wú)參構(gòu)造器金拒,防止類(lèi)外進(jìn)行實(shí)例化。
public class CEO{
public static final CEO INSTANCE = new CEO();
private CEO(){}
}
但是,無(wú)參私有構(gòu)造器可以通過(guò)反射的setAccessible()修改
解決這個(gè)問(wèn)題則需要在無(wú)參構(gòu)造器中加入判斷绪抛,當(dāng)進(jìn)行第二個(gè)實(shí)例化的時(shí)候拋出異常资铡。還以提供友好的getInstance()公共靜態(tài)方法。
如果該類(lèi)實(shí)現(xiàn)了序列化幢码,那么需要重寫(xiě)readResolve,防止反序列化時(shí)創(chuàng)建一個(gè)新的實(shí)例:
private Object readResolve(){
return INSTANCE;
}
通過(guò)枚舉實(shí)現(xiàn)Singleton笤休,無(wú)償提供序列化機(jī)制,絕對(duì)防止多次實(shí)例化症副,簡(jiǎn)潔的同時(shí)功能和公有域很相似店雅。
public enum CEO{
INSTANCE;
public void speech(){....}
public void work(){....}
}
工具類(lèi)中加入私有無(wú)參構(gòu)造器,防止實(shí)例化是個(gè)很好的習(xí)慣贞铣。
4. 避免不必要的對(duì)象創(chuàng)建
- 不要使用 new String("text");闹啦,這實(shí)際上創(chuàng)建了兩個(gè)實(shí)例。
- 能重用對(duì)象的時(shí)候盡量重用對(duì)象辕坝,但是需要避免重用對(duì)象造成的錯(cuò)誤窍奋,得不償失。
- 避免不必要的額自動(dòng)裝箱工作酱畅,能用原生類(lèi)型就用原生類(lèi)型琳袄。
- 慎重使用對(duì)象池,維護(hù)起來(lái)需要很多工作纺酸。
5. 消除過(guò)期的對(duì)象的引用
- 在一下設(shè)計(jì)的Stack出棧方法中:
public Object pop(){
if(size==0)
throw new EmptyStackExcetiop();
return elements[--size];
}
經(jīng)過(guò)多次push增長(zhǎng)后窖逗,pop掉的元素依然在Stack中保持有過(guò)期引用并由Stack管理著,而這些過(guò)期引用后期不在被使用餐蔬,卻不能被垃圾回收器進(jìn)行回收碎紊。導(dǎo)致內(nèi)存泄露》担可以做一下修改:
public Object pop(){
if(size==0)
throw new EmptyStackExcetiop();
Object o = elements[--size];
element[size] = null;
return o;
}
設(shè)計(jì)自己管理內(nèi)存的時(shí)候矮慕,都需要警惕內(nèi)存泄露帶來(lái)的問(wèn)題
- 緩存帶來(lái)內(nèi)存泄露可能是緩存引用不在使用但是仍然長(zhǎng)時(shí)間留在緩存中。
- 當(dāng)設(shè)計(jì)的緩存項(xiàng)的生命周期由該鍵的外部引用決定時(shí)可以使用WeakHashMap
- 可以使用Timer或者ScheduleThreadPoolExecutor設(shè)計(jì)一個(gè)后臺(tái)線(xiàn)程進(jìn)行引用清理工作
- LinkedHashMap的removeEldsetEntry管理緩存啄骇,實(shí)現(xiàn)添加新緩存項(xiàng)的同時(shí)清除舊的緩存項(xiàng)
- 回調(diào)與監(jiān)聽(tīng)導(dǎo)致的內(nèi)存泄露是因?yàn)椋皇褂玫臅r(shí)候沒(méi)有顯示的取消注冊(cè)瘟斜,否則不斷的注冊(cè)回調(diào)或者監(jiān)聽(tīng)會(huì)不斷積累缸夹。可以只保存監(jiān)聽(tīng)的弱引用螺句。
6. 避免使用終結(jié)方法
- 避免盡量避免使用finalize()終結(jié)方法虽惭,因?yàn)閖vm不能保證何時(shí)執(zhí)行,或者是否執(zhí)行蛇尚,執(zhí)行需要多長(zhǎng)時(shí)間芽唇。
- 涉及資源釋放的的時(shí)候,盡可能的使用顯示終結(jié)方法,例如流操作中的close()方法匆笤,一般使用try..catch..finally研侣,并在finally中調(diào)用close()操作。
- 但是finalize方法可以作為最后的安全網(wǎng)操作炮捧,也就是在不顯示終結(jié)的時(shí)候提供最后的終結(jié)調(diào)用庶诡,長(zhǎng)時(shí)間釋放資源總比不釋放資源好∨乜危或者是用來(lái)釋放非關(guān)鍵本地自愿的額時(shí)候使用末誓。
子類(lèi)覆蓋了父類(lèi)的finalize方法,那么需要顯示調(diào)用父類(lèi)的finalize书蚪,super.finalize();