Effective Java:创建和销毁对象
前言:
? 讀這本書第1條規(guī)則的時(shí)候就感覺到這是一本很好的書,可以把我們的Java功底提升一個(gè)檔次,我還是比較推薦的。本博客是針對(duì)《Effective Java》這本書第2章所寫的一篇讀書筆記。博客中也有會(huì)一些個(gè)人對(duì)某個(gè)模塊的理解和深入探究,希望與大家一起進(jìn)步。
概述:
? 本章的主題是創(chuàng)建和銷毀對(duì)象:何時(shí)以及如何創(chuàng)建對(duì)象,何時(shí)以及如何避免創(chuàng)建對(duì)象,如何確保它們能夠適時(shí)地銷毀,以及如何管理對(duì)象銷毀之前必須進(jìn)行的各種清理動(dòng)作。
相關(guān)內(nèi)容:
1.考慮用靜態(tài)工廠方法代替構(gòu)造器
思路解析:
? 如果有人問你,如何去初始化一個(gè)類的對(duì)象實(shí)例時(shí),你的第一反應(yīng)可能就是去實(shí)現(xiàn)類的構(gòu)造器。那如果我告訴你,這樣的做法,其實(shí)并不是最好的方法,你可能會(huì)感覺意想不到,不會(huì)啊,老師和書上都是這樣寫的啊!怎么可能不去實(shí)現(xiàn)類的構(gòu)造器就可以初始化一個(gè)類的對(duì)象實(shí)例呢?
? 辦法還真有!如果你學(xué)習(xí)過一些設(shè)計(jì)模式(這里是廢話了,如果你學(xué)習(xí)過設(shè)計(jì)模式,那你肯定早就知道是怎么回事了),比如單件模式。單件模式會(huì)涉及到一個(gè)這樣的問題:如何讓我們類的對(duì)象實(shí)現(xiàn)只被初始化一次呢?你可以使用類的構(gòu)造器小小地實(shí)踐一下,不管是打日志還是Debug,我想答案是一樣的,不可能完成!這時(shí),你就需要了解和學(xué)習(xí)用靜態(tài)工廠方法來代替構(gòu)造器了,你可以模仿以下實(shí)現(xiàn)。
public static ClassA getInstance() {return new ClassA; } ? 看到以上代碼,可能你會(huì)有所抱怨,不對(duì)啊,上面的方法沒有達(dá)成我們需求中的效果啊,你一定是在耍我!
? 先不要著急,上面的方法的確只是一個(gè)思路過程,如果你想讓你的對(duì)象只對(duì)被實(shí)例化一次,你可以模仿以下代碼:
public static ClassA getInstance() {if(mInstance == null) {mInstance = new ClassA();}return mInstance; } ? 我想實(shí)現(xiàn)了以上代碼的你可能會(huì)很開心,這樣的確可以只實(shí)例化一個(gè)對(duì)象了。如果你的好奇心足夠強(qiáng)大的話,我想你還應(yīng)該嘗試一下,使用構(gòu)造器來重新實(shí)例一次或N次。還是可以實(shí)例化很多個(gè)對(duì)象實(shí)例,對(duì)不對(duì)?別擔(dān)心,我沒有騙你,是要利用以上的靜態(tài)工廠方法,不過你還有一件小事沒有去完成,那就是屏蔽默認(rèn)的構(gòu)造方法。像下面這樣的: private ClassA() {// do something!!! } ? 沒錯(cuò),就是這樣,你可以在私有的構(gòu)造器中添加一些你想添加的,這都沒有關(guān)系,但是請(qǐng)保證它的私有性。 可能看到這里,你沒有明白一個(gè)靜態(tài)的工廠方法會(huì)給我們帶來什么樣的好處。下面就是對(duì)靜態(tài)工廠方法的優(yōu)點(diǎn)介紹。
靜態(tài)工廠方法的優(yōu)點(diǎn):
? a.它們有名稱:你可以使用對(duì)方法的合理命名來提示用戶,這一次被創(chuàng)建出來的是什么樣的對(duì)象。
? b.不必在每次調(diào)用它們的時(shí)候都創(chuàng)建一個(gè)新的對(duì)象:這一點(diǎn)上面的單件模式也有提到,這一優(yōu)點(diǎn)的優(yōu)勢(shì)在于單件模式本身實(shí)在的需求和節(jié)約資源開支。
? c.可以返回任何子類型的對(duì)象:這種靈活性的一種應(yīng)用是,API可以返回對(duì)象,同時(shí)又不會(huì)使對(duì)象的類變成公有的。以這種方式隱藏實(shí)現(xiàn)類會(huì)使API變得非常簡(jiǎn)潔。
? d.輕松創(chuàng)建相同參數(shù)的不同對(duì)象:想像一下,如果我們想要?jiǎng)?chuàng)建兩個(gè)不同的對(duì)象,而這兩個(gè)對(duì)象在創(chuàng)建的過程中都接受一個(gè)int型的參數(shù),請(qǐng)問要如何創(chuàng)建?靜態(tài)工廠方法就是答案!
靜態(tài)工廠方法的缺點(diǎn):
? a.類如果不含有僅有的或是受保護(hù)的構(gòu)造器,就不能被子類化:這里你可能會(huì)困惑,為什么這樣會(huì)構(gòu)成一個(gè)缺點(diǎn)?仔細(xì)一想,你肯定會(huì)原來如此。
? b.它們與其他的靜態(tài)方法實(shí)際上沒有任何區(qū)別。
2.遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用構(gòu)建器
? 對(duì)于這一點(diǎn)我還是保留對(duì)大家的信任,大家都已經(jīng)熟練掌握了它的使用和好處。它會(huì)使用在這樣一個(gè)必要的環(huán)境下:如果你想構(gòu)造一個(gè)類的對(duì)象實(shí)例,而這個(gè)類在構(gòu)造器上需要傳遞很多參數(shù),這里我們假設(shè)有10個(gè)。是不是想想就覺得很可怕,它充斥著我們的大腦,并抗?fàn)幹f這樣很麻煩,我們不要這樣來做吧。而看起來麻煩還是小事,只要你耐心一點(diǎn),總能渡過,不過如果你要把兩個(gè)或N個(gè)參數(shù)給弄混了,那我想后果一定很“精彩”。
? 在這種情境下,我們可以考慮一下使用構(gòu)建器。別被構(gòu)建器的名稱給嚇到了,如果我換一個(gè)說法,相信你就會(huì)明白了。那就是把合適的參數(shù)封裝成一個(gè)類,在類中使用setter和getter來實(shí)現(xiàn)需求。
? 這樣的做法不僅可以讓代碼更簡(jiǎn)潔,還不容易讓參數(shù)混淆,真是一個(gè)好東西啊。
3.用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性
? 在第1條中我們說到了關(guān)于單件模式的一些使用場(chǎng)景,下面會(huì)有一些額外介紹。如果你對(duì)單件模式還有一些想要了解的,可以在Java設(shè)計(jì)模式中進(jìn)行了解。
? 對(duì)于如何使用私有構(gòu)造器優(yōu)化Singleton屬性我們?cè)诘?條和Java設(shè)計(jì)模式的《Java設(shè)計(jì)模式——單件模式》中都有介紹,這里不再贅述。
? 關(guān)于如何使用枚舉來強(qiáng)化Singleton屬性,我們可以像下面這樣:
public enum Elvis {INSTANCE;public void leaveTheBuilding(){...} } 這種方法和公有域方法相近,但是它更加簡(jiǎn)潔,無償?shù)靥峁┝诵蛄谢瘷C(jī)制,絕對(duì)防止多次實(shí)例化。雖然這種方法還沒有廣泛采用,但是單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法。
4.避免創(chuàng)建不必要的對(duì)象
? 從節(jié)省資源的角度考慮,我們應(yīng)該盡量避免創(chuàng)建不必要的對(duì)象。看到這里,你可能會(huì)說上面提到的單件模式算不算這個(gè)避免創(chuàng)建不必要的對(duì)象呢?我說它是,又不全是。因?yàn)樗粏问且驗(yàn)橐苊鈩?chuàng)建不必要的對(duì)象而設(shè)計(jì)的,更多的是它要實(shí)現(xiàn)的是只能有一個(gè)對(duì)象。
? 你是否會(huì)像這樣來創(chuàng)建一個(gè)字符串:
? 可能你會(huì)說你沒有,可是你可能很少會(huì)想到這樣的一種方式來初始化一個(gè)字符串對(duì)象的實(shí)例。但是我想要說的是最好不要像上面那樣創(chuàng)建,因?yàn)樗谝粋€(gè)多次創(chuàng)建中會(huì)產(chǎn)生很多不必要的實(shí)例。
? 你可以這樣來改進(jìn)它,使之更加合理:
String s = "abcdefghi"; ? 在Java的機(jī)制中,上面的這個(gè)版本只用了一個(gè)String實(shí)例,對(duì)于所有在同一臺(tái)虛擬機(jī)中運(yùn)行的代碼,只要它們包含的字符串字面常量,該對(duì)象就會(huì)被重用。
5.消除過期的對(duì)象引用
? 在項(xiàng)目?jī)?yōu)化的時(shí)候,我們經(jīng)常可能要去做內(nèi)存泄漏的檢測(cè)。不要以為Java已經(jīng)有了垃圾回收的機(jī)制,我們就可以坐享其成,不再去考慮內(nèi)存管理的事情了。如果你想了解更多內(nèi)容,可以去看《Android開發(fā)中,可能會(huì)導(dǎo)致內(nèi)存泄漏的問題》這篇文章。
? 下面請(qǐng)看這個(gè)例子:Can you spot the "memory leak"?
public class Stack {private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() {elements = new Object[DEFAULT_INITIAL_CAPACITY];}public void push(Object e) {ensureCapacity();elements[size++] = e;}public Object pop() {if (size == 0) {throw new EmptyStackException();}return elements[--size];}private void ensureCapacity() {if(elements.length == size) {elements = Arrays.copyOf(elements, 2 * size + 1);}} }
? 你能看出這段代碼的問題所在嗎?
? 我們?cè)囅胍幌?#xff0c;如果我們的棧在先增加元素,然后再收縮。那么,這時(shí)從棧中pop的對(duì)象不會(huì)被當(dāng)作垃圾回收,即使用棧的程序不再引用它們,它們也不會(huì)被回收。這是因?yàn)?#xff0c;棧內(nèi)部維護(hù)著這些對(duì)象的過期引用。這么說,可能你還不清楚什么是過期引用。換句話說吧,就是說我們的棧還在,這些被pop的對(duì)象曾經(jīng)是屬于這個(gè)棧的,它們都還持有這個(gè)活動(dòng)棧的引用,那么Java的垃圾回收機(jī)制就不會(huì)對(duì)它們?cè)趺礃恿恕?/span>
? 這個(gè)就有一點(diǎn)像Android中,初始化了很多持有生命周期較長(zhǎng)的Context的對(duì)象,而這些對(duì)象由于始終持有Context的引用,所以不會(huì)被Java回收機(jī)制回收。
? 這類問題的解決方法也很簡(jiǎn)單:一旦對(duì)象引用已經(jīng)過期,只需清空這些引用即可。根據(jù)這一點(diǎn),改進(jìn)的程序如下:
public Object pop() {if (size == 0) {throw new EmptyStackException();}Object result = elements[--size];elements[size] = null;return result;}? 清空過期引用的另一個(gè)好處是,如果它們以后又被錯(cuò)誤地解除引用,程序就會(huì)立即拋出一個(gè)NullPointerException的異常,而不是悄悄地錯(cuò)誤運(yùn)行下去。
總結(jié)
以上是生活随笔為你收集整理的Effective Java:创建和销毁对象的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 远程连接CentOS的MySQL报错:C
- 下一篇: CentOS 6.6编译安装LAMP(A