第2章 創(chuàng)建和銷毀對象

何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象
如何確保適時銷毀,如何管理對象銷毀前必要的清理

第1條 考慮用靜態(tài)工廠方法代替構(gòu)造器

靜態(tài)工廠方法是一個返回類的實(shí)例的靜態(tài)方法,對它其實(shí)只是一個普通的靜態(tài)方法而已,需要注意的是它與設(shè)計(jì)模式中的工廠方法不同,不要弄混淆了

Java源碼中的例子:

 public static Boolean valueOf(boolean b){
    return b?Boolean.TRUE:Boolean.FALSE;
 }

優(yōu)勢

列舉靜態(tài)工廠方法與構(gòu)造器相比,有哪些優(yōu)勢

靜態(tài)工廠方法有名稱

構(gòu)造方法沒有名稱,給靜態(tài)工廠取一個適當(dāng)?shù)姆椒Q會更易于閱讀,也更易于客戶端使用.

特別是在面對有多個參數(shù)或者多個構(gòu)造方法的類的時候,會讓客戶端不知道如何選擇構(gòu)造方法(自行腦補(bǔ)一個大寫的懵B).

不必在每次調(diào)用它們的時候都創(chuàng)建一個新對象

我們知道構(gòu)造方法每次都會創(chuàng)建一個新的實(shí)例,而當(dāng)我們需要避免重復(fù)創(chuàng)建不必要的對象(或者不可變的類)時通過構(gòu)造方法去做是做不到的,而靜態(tài)方法可以為重復(fù)的調(diào)用返回相同的對象(比如返回的對象需要是一個單例)

它們可以返回原返回類型的任何子類型的對象

構(gòu)造方法只能返回類本身,而靜態(tài)方法可以返回它的子類,利用多態(tài)的特性使得靜態(tài)方法更加靈活
比如Executors類的各種newxxx方法返回ExecutorService的子類

在創(chuàng)建參數(shù)化類型實(shí)例的時候,它們使代碼變得更加簡潔

調(diào)用參數(shù)化構(gòu)造器時,即使類型參數(shù)很明顯也必須指明.
書中列舉了實(shí)例化HashMap的例子:

Map<String,List<String>>m = new HashMap<Stirng,List<String>>();

如果用靜態(tài)工廠可以這么寫:

public static <K,V>HashMap<K,V>newInstance(){
    reutrn new HashMap<K,V>();
}

想想以前也確實(shí)煩這些類型指定,一寫一長串,但是現(xiàn)在的JVMS(Java1.7)已經(jīng)實(shí)現(xiàn)了類型推導(dǎo),所以其實(shí)也沒那么復(fù)雜了.

劣勢

靜態(tài)工廠方法的劣勢

類如果不含公有的或者受保護(hù)的構(gòu)造器,就不能被子類化

它們與其他的靜態(tài)方法實(shí)際上沒有任何區(qū)別

確實(shí),靜態(tài)方法真的只是一個靜態(tài)的方法而已,如果文檔上不提到創(chuàng)建該類的實(shí)例可以使用靜態(tài)方法的時候,很容易被忽略

所以能我們需要遵守一定的命名習(xí)慣:

靜態(tài)方法推薦的命名名稱
  • valueOf 返回的實(shí)例與參數(shù)具有相同的值,一般用于類型轉(zhuǎn)換(如String Boolean Double等等)
  • of valueOf的簡潔代替
  • getInstance 返回的實(shí)例工具參數(shù)而定,對于單例,則一般沒有參數(shù),并且每次返回的都是同一個實(shí)例
  • newInstance 每次返回一個新的實(shí)例
  • getType
  • newType

不能很好的擴(kuò)展到大量的可選參數(shù)

當(dāng)參數(shù)非常多的時候靜態(tài)方法的可讀性,維護(hù)性等就非常差了,這個后面會說

實(shí)際運(yùn)用

現(xiàn)在在我們新建一個Fragment的時候,AS會自動生成一個newInstance(String ,String )的一個方法,這個就是靜態(tài)工廠方法了,可見官方也推薦我們使用靜態(tài)工廠方法.

另外Java源碼中也有非常多的靜態(tài)工廠方法的運(yùn)用,比如上面提及的Executors,自己去體會~

小結(jié)

靜態(tài)工廠方法與構(gòu)造器各有優(yōu)勢,不過一般來說靜態(tài)工廠通常更加合適,所以以后多考慮使用靜態(tài)工廠吧.

第2條 遇到多個構(gòu)造器參數(shù)時要考慮用構(gòu)建器(Builder)

當(dāng)實(shí)例化一個類時有很多可選參數(shù)的時候,我們或許會寫很多不同參數(shù)的構(gòu)造方法,可讀性會非常差,并且客戶端也不知道在什么時候用什么構(gòu)造器,這是非常非常糟糕的,同樣靜態(tài)方工廠也不能避免,這個時候我們需要用到構(gòu)建器,也即設(shè)計(jì)模式里的Builder模式

Builder模式非常常見了,列舉個列子就過了:

        AlertDialog dialog = new AlertDialog.Builder(this)
                .setCancelable(true)
                //...各種可選參數(shù) 想要什么設(shè)置什么
                .setMessage("Builder模式")
                .create();

Builder模式雖然能解決多參數(shù)遇到的問題,但是它也有缺點(diǎn):
為了創(chuàng)建對象,我們必須先創(chuàng)建一個Builder,這是一個多余的開銷,雖然開銷并不明顯,但確實(shí)存在.

小結(jié)

當(dāng)有多個參數(shù)的時候,Builder模式是非常不錯的選擇.

第3條 用私有構(gòu)造器或者枚舉類型強(qiáng)化Singleton屬性

所謂Singleton即單例,指僅僅被實(shí)例化一次的類,這一條與單例息息相關(guān).

私有構(gòu)造器強(qiáng)化

寫過單例的一定知道,構(gòu)造器一定要私有,否則別人隨便new,怎么保證單例呢?

書中還講到了單例如何防反射,防反序列化,這里就不提了

枚舉強(qiáng)化

java 1.5 版本之后,單例多了一個實(shí)現(xiàn)方法(包含單個元素的枚舉類型):

public enum Elvis{
    INSTANCE;
    public void leaveTheBuilding(){...}
}

優(yōu)勢:

  • 無償提供序列化機(jī)制
  • 絕對防止多次實(shí)例化
  • 防反射
  • 簡潔

書中說:單元素的枚舉類型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法,可惜的是我沒在實(shí)際中用過,也沒見過這種實(shí)現(xiàn)方式.

小結(jié)

單例有很多實(shí)現(xiàn)方式,也是不容易的~

第4條 通過私有構(gòu)造器強(qiáng)化不可實(shí)例化的能力

首先理解什么是不可實(shí)例化?
不可實(shí)例化是指只包含靜態(tài)方法和靜態(tài)域的類
比如BitmapUtil等各種Utils,它們包含各種靜態(tài)方法,但是實(shí)際上我們不會也不需要去實(shí)例化它!

靜態(tài)域(static-field) 比如:public static Object obj

既然工具類不希望被實(shí)例化,那么如何做呢?

提供一個私有構(gòu)造器,并在里面拋出異常即可

eg:

public class Util{
  private Util{
   // Suppress default constructor for noninstantiability
   throw new AssertionError();
  }
}

相信都會感覺這多此一舉,誰tm沒事去實(shí)例化工具類啊,以前我也這么覺得,直到我的膝蓋中了...

偶不,直到我在優(yōu)秀的庫中看到這寫法,比如RxJava中的Subscriptions類:

public final class Subscriptions {
    private Subscriptions() {
        throw new IllegalStateException("No instances!");
    }
    //...
}

又如Jake大神的RxBinding中各種RxXXXX也是如此:

private RxAppBarLayout() {
    throw new AssertionError("No instances.");
  }

優(yōu)秀的項(xiàng)目喘漏,細(xì)節(jié)處理都非常優(yōu)秀

小結(jié)

有時候有些東西非常有道理,只是自己太弱小,悟不到而已,虛心學(xué)習(xí),keep growing

PS: EffectiveJava是本好書,RxJava是個優(yōu)秀的庫,Jake是真大神T欧帧!

第5條 避免創(chuàng)建不必要的對象

一般的講,最后能重用對象而不是在每次需要的時候創(chuàng)建一個相同功能的新對象.
另外對于一個不可變的對象(immutable 后面會講),它始終可以被重用.

作者舉了幾個例子來說明:

創(chuàng)建String實(shí)例

String s = "stringette";替代String s = new String("stringette")
因?yàn)楹笳叩膮?shù)其實(shí)就是一個實(shí)例,每一次調(diào)用都會多創(chuàng)建一個沒用的對象.

Boolean

    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);

Boolean.valueOf()方法重用了FALSETRUE來避免重復(fù)創(chuàng)建相同功能的對象

自動裝箱autoboxing

自動裝箱允許我們將基本類型和裝箱基本類型(Boxed Primitive Type)混用,按需自動裝箱和拆箱

它們倆之間性能是有明顯的差別的(基本類型更優(yōu))

eg:

public static void main(String[] args){
    Long sum = 0L;
    for(long i=0;i<Integer.MAX_VALUE;i++){
        sum += i;
    }
    System.out.println(sum);
}

sum 的類型為Long,這樣會比long多創(chuàng)建約2的31次方的Long實(shí)例,影響性能,所需時間大約是long的6倍多,非常可怕啊

so,記住,優(yōu)先使用基本類型!

PS 這里要說的不是創(chuàng)建對象非常昂貴,因?yàn)?em>小對象的創(chuàng)建和回收是非常廉價的

對象池(object pool)

維護(hù)對象池來避免創(chuàng)建對象只針對非常重量級的對象,如數(shù)據(jù)庫連接池

Android中對象池有很多,如Message類,又如Glide中的Bitmap池

對象池的缺點(diǎn)

  1. 代碼更亂 涉及到回收、重用必然會增加許多代碼
  2. 增加內(nèi)存占用,損害性能

小結(jié)

當(dāng)需要重用的時候,就不要創(chuàng)建

第6條 消除過期的對象引用

首先需要明確,Java即使有GC,我們依然要自己考慮內(nèi)存管理的事情.

當(dāng)一個數(shù)組擴(kuò)容后又縮減,比如size從0->200->100(一個棧先增長,后收縮),那么元素的index>=100的那些元素(被pop掉的)都算是過期元素,那些引用就是過期引用(永遠(yuǎn)不會再被解除的引用)

過期引用導(dǎo)致了內(nèi)存泄露

雖然對于自己來說pop掉的元素我們不會去用,但是由于過期引用的存在,GC并不會去回收它們,所以需要我們手動清空這些引用.

eg:

public Object pop(){
    if(size==0) throw new EmptyStackException();
    Object result = elements[--size];
    elements[size] = null; //Eliminate obsolete reference
    return result;
}

PS:書中還提到了 磁盤交換(Disk Paging)-->wiki
備注:虛擬內(nèi)存

第7條 避免使用終結(jié)方法

終結(jié)方法:finalizer (老實(shí)說,這個真沒用過)

沒看懂,記錄一些點(diǎn)..

  • 終結(jié)方法會導(dǎo)致行為不穩(wěn)定,降低性能,以及可移植性問題
  • 不能保證會被及時地執(zhí)行,而且根本不保證它們會被執(zhí)行(這..好過分..)

還是沒看懂,被自己蠢哭了..

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末家浇,一起剝皮案震驚了整個濱河市躯畴,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌矛物,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跪但,死亡現(xiàn)場離奇詭異履羞,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)屡久,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進(jìn)店門忆首,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人被环,你說我怎么就攤上這事糙及。” “怎么了筛欢?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵浸锨,是天一觀的道長。 經(jīng)常有香客問我版姑,道長柱搜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任剥险,我火速辦了婚禮聪蘸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘表制。我一直安慰自己健爬,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布么介。 她就那樣靜靜地躺著娜遵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夭拌。 梳的紋絲不亂的頭發(fā)上魔熏,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天衷咽,我揣著相機(jī)與錄音鸽扁,去河邊找鬼。 笑死镶骗,一個胖子當(dāng)著我的面吹牛桶现,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鼎姊,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼骡和,長吁一口氣:“原來是場噩夢啊……” “哼相赁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慰于,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钮科,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后婆赠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绵脯,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年休里,在試婚紗的時候發(fā)現(xiàn)自己被綠了蛆挫。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡妙黍,死狀恐怖悴侵,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拭嫁,我是刑警寧澤可免,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站做粤,受9級特大地震影響巴元,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜驮宴,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一逮刨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧堵泽,春花似錦修己、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至纹安,卻和暖如春尤辱,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背厢岂。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工光督, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人塔粒。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓结借,卻偏偏與公主長得像,于是被迫代替她去往敵國和親卒茬。 傳聞我的和親對象是個殘疾皇子船老,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評論 2 355

推薦閱讀更多精彩內(nèi)容