《Effective Java》讀書(shū)筆記 —— 對(duì)象的創(chuàng)建和銷(xiāo)毀

本文主題是創(chuàng)建和銷(xiāo)毀對(duì)象房待,關(guān)注一下幾個(gè)問(wèn)題:

  • 何時(shí)以及如何創(chuàng)建對(duì)象
  • 何時(shí)以及如何避免創(chuàng)建對(duì)象
  • 如何去報(bào)它們能夠適時(shí)銷(xiāo)毀
  • 如何管理對(duì)象銷(xiāo)毀之前必須進(jìn)行的各種清理動(dòng)作

1.考慮使用靜態(tài)工廠方法代替構(gòu)造器(靜態(tài)工廠模式)

創(chuàng)建類(lèi)實(shí)例的方式有兩種:

  • 公有的構(gòu)造器
  • 公有的靜態(tài)工廠方法
靜態(tài)工廠方法
  • 優(yōu)勢(shì)

    • 靜態(tài)工廠方法與構(gòu)造器不同的第一大優(yōu)勢(shì)在于攀细,它們有名稱效扫,如果構(gòu)造器的參數(shù)本身沒(méi)有確切的描述返回的對(duì)象,那么適當(dāng)名稱的靜態(tài)工廠會(huì)更加合適
    • 不必每次調(diào)用它們的時(shí)候都創(chuàng)建一個(gè)新對(duì)象俏脊,使得不可變對(duì)象可以使用預(yù)先構(gòu)建好的實(shí)例,利用緩存實(shí)例進(jìn)行復(fù)用,為重復(fù)的調(diào)用返回相同的對(duì)象怀喉,如果創(chuàng)建對(duì)象的代價(jià)很高,這個(gè)技術(shù)可以極大提升性能
    • 可以返回類(lèi)型的任何子類(lèi)型的對(duì)象船响,在選擇返回對(duì)象時(shí)有更大的靈活性躬拢。
      • 可以返回非公有對(duì)象,同時(shí)又不會(huì)使對(duì)象的類(lèi)變成公有的见间,隱藏實(shí)現(xiàn)類(lèi)
      • 公有的靜態(tài)工廠方法所返回對(duì)象的類(lèi)不僅可以是非公有的聊闯,而且該類(lèi)可以對(duì)著每次調(diào)用而發(fā)生變化,取決于靜態(tài)工廠方法的參數(shù)值(工廠方法模式)
    • 創(chuàng)建參數(shù)化類(lèi)型(泛型)實(shí)例時(shí)米诉,使代碼變得更加簡(jiǎn)潔
  • 缺點(diǎn)

    • 類(lèi)如果不含有公有或者受保護(hù)構(gòu)造器菱蔬,就不能被子類(lèi)化
    • 與其他的靜態(tài)方法實(shí)際上沒(méi)有任何區(qū)別
  • 靜態(tài)工廠方法命名規(guī)范

    • valueOf
      • 該方法返回的實(shí)例與它的參數(shù)具有相同的值,這樣的靜態(tài)工廠方法實(shí)際上是類(lèi)型轉(zhuǎn)換方法
    • of
    • getInstance
      • 返回的實(shí)例通過(guò)方法的參數(shù)來(lái)描述,如果沒(méi)有參數(shù)拴泌,則返回唯一的單例
    • newInstance
      • 確保返回的實(shí)例都與其他實(shí)例不同
    • getType
    • newType

2.遇到多個(gè)構(gòu)造器參數(shù)時(shí)要考慮用構(gòu)建器(Builder創(chuàng)建者模式)

靜態(tài)工廠和構(gòu)造器有個(gè)共同的局限性魏身,不能很好地?cái)U(kuò)展到大量可選參數(shù)。

處理有大量可選參數(shù)的構(gòu)造器的方式:

  • 重疊構(gòu)造器
  • JavaBeans 模式
  • Builder 模式
重疊構(gòu)造器

提供第一個(gè)只有必要參數(shù)的構(gòu)造器蚪腐,第二個(gè)構(gòu)造器有一個(gè)可選參數(shù)箭昵,第三個(gè)有兩個(gè)可選參數(shù),以此類(lèi)推回季,最后一個(gè)構(gòu)造器包含所有可選參數(shù)家制。

public NutritionFact(int servingSize, int servings){}
public NutritionFact(int servingSize, int servings, int calories){}
public NutritionFact(int servingSize, int servings, int calories, int fat){}
public NutritionFact(int servingSize, int servings, int calories, int fat, int sodium){}
...

缺點(diǎn):重疊構(gòu)造器模式可行,但是當(dāng)有許多參數(shù)的時(shí)候泡一,客戶端代碼會(huì)很難編寫(xiě)和難以閱讀

JavaBeans 模式

另一種替代方法颤殴,JavaBeans 模式,調(diào)用一個(gè)無(wú)參構(gòu)造器來(lái)創(chuàng)建對(duì)象鼻忠,然后調(diào)用setter方法設(shè)置每個(gè)必要參數(shù)涵但,以及每個(gè)相關(guān)的可選參數(shù)。

NutritionFact cocoCola = new NutritionFact();
cocoCola.setServingSize(240);
cocoCola.setServings(8);
cocoCola.setCalories(100);
cocoCola.setSodium(35);
cocoCola.setCarbohydrate(27);

缺點(diǎn):

  • 構(gòu)造過(guò)程被分到幾個(gè)調(diào)用中帖蔓,構(gòu)造過(guò)程 JavaBean 可能處于不一致的狀態(tài)矮瘟。類(lèi)無(wú)法僅僅通過(guò)校驗(yàn)構(gòu)造器參數(shù)的有效性來(lái)保證一致性
  • JavaBean 模式阻止了把類(lèi)做成不可變的可能,需要確保它的線程安全
Builder 模式

不直接生成想要的對(duì)象讨阻,客戶端利用多有必要的參數(shù)調(diào)用構(gòu)造器(或靜態(tài)工廠)得到一個(gè)builder對(duì)象芥永,然后客戶端再builder 對(duì)象上調(diào)用類(lèi)似setter方法,來(lái)設(shè)置每個(gè)相關(guān)的可選參數(shù)钝吮,最后客戶端調(diào)用無(wú)參的build方法來(lái)生成不可變的對(duì)象埋涧,這個(gè)builder是類(lèi)的靜態(tài)成員類(lèi)。

public class NutritionFacts {
    private final int calories = 0;
    private final int fat = 0;
    private final int sodium = 0;
       
    // 靜態(tài)內(nèi)部類(lèi) Builder 對(duì)象 
    public static class Builder {
        private int calories = 0;
        private int fat = 0;
        private int sodium = 0;
    
        // setter方法返回當(dāng)前builder對(duì)象奇瘦,方便鏈?zhǔn)秸{(diào)用
        public Builder setCalories(int val) {
            calories = val;
        }
          
        public Builder setFat(int val) {
            fat = val;
        }  
        
        public Builder setSodium(int val) {
            sodium = val;
        }
        
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }
    
    // 傳入 Builder 對(duì)象的構(gòu)造方法
    public NutritionFacts(Builder builder) {
        calories = builder.calories;
        fat = builder.fat;
        sodium = builder.sodium;
    }
}

缺點(diǎn):需要額外開(kāi)銷(xiāo)

總結(jié)

如果類(lèi)的構(gòu)造器或者靜態(tài)工廠中具有多個(gè)參數(shù)棘催,設(shè)計(jì)這種類(lèi)時(shí),Builder 模式就是不錯(cuò)的選擇耳标。

3.用私有構(gòu)造器或者枚舉類(lèi)型強(qiáng)化Singleton屬性(單例模式)

Singleton 指僅僅被實(shí)例化一次的類(lèi)醇坝。

實(shí)現(xiàn)Singleton的方式有很多種

方式一

把構(gòu)造器保持為私有的,并導(dǎo)出公有的靜態(tài)成員次坡,并且靜態(tài)成員是個(gè)final的

public class Elvis {
    public static final Elvis INSTANCE = new Elvis();
    private Elvis() {...};
    
}

問(wèn)題:無(wú)法抵御通過(guò)反射調(diào)用私有構(gòu)造器的攻擊呼猪。

方案:可以修改構(gòu)造器,讓它在要求創(chuàng)建第二個(gè)實(shí)例的時(shí)候拋出異常砸琅。

方式二

方式二中宋距,公有成員不再是屬性,而是一個(gè)靜態(tài)方法getInstance

public class Elvis {
    pvivate static final Elvis INSTANCE = new Elvis();
    private Elvis() {...};
    
    public static Elvis getInstance() {return INSTANCE;}
    
}

問(wèn)題:如果此類(lèi)實(shí)現(xiàn)了序列化症脂,序列化之后的結(jié)果都會(huì)創(chuàng)建一個(gè)新的實(shí)例谚赎。
方案:重寫(xiě)readResolve方法

private Object readResolve() {
    return INSTANCE;
}
方式三

編寫(xiě)一個(gè)包含單個(gè)元素的枚舉類(lèi)型淫僻。

public enum Elvis {
    INSTANCE;
}

與公有方法相近,但更加簡(jiǎn)潔壶唤,無(wú)償?shù)奶峁┝诵蛄谢瘷C(jī)制雳灵,并且防止序列到導(dǎo)致多次實(shí)例化,并且防止反射的攻擊闸盔。

總結(jié)

單元素的枚舉類(lèi)型已經(jīng)成為實(shí)現(xiàn)Singleton的最佳方法悯辙。

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

工具類(lèi)不希望被實(shí)例化,實(shí)例對(duì)它沒(méi)有任何意義蕾殴。

在缺少顯示構(gòu)造器時(shí)笑撞,編譯器會(huì)自動(dòng)提供一個(gè)公有的岛啸,無(wú)參的缺省構(gòu)造器钓觉。

可通過(guò)創(chuàng)建私有構(gòu)造器,并構(gòu)造器中拋出異常坚踩,來(lái)避免實(shí)例化此類(lèi)荡灾。

5.避免創(chuàng)建不必要的對(duì)象

一般來(lái)說(shuō),最好能重用對(duì)象而不是在每次需要的時(shí)候就創(chuàng)建一個(gè)相同功能的新對(duì)象瞬铸。如果對(duì)象是不可變的批幌,那么它就應(yīng)該始終被重用。

舉例一
String s = new String("mystring")  // 每次都會(huì)創(chuàng)建一個(gè)新的String實(shí)例
String s = "mystring"           // 推薦嗓节,保證在同一臺(tái)虛擬機(jī)中運(yùn)行的代碼荧缘,只要包含相同的字符串字面常量,就會(huì)被重用
舉例二:不可變類(lèi)

對(duì)于不可變類(lèi)拦宣,優(yōu)先使用靜態(tài)工廠方法截粗,每次調(diào)用可以重用,避免創(chuàng)建不必要的對(duì)象鸵隧。

舉例三

通過(guò)靜態(tài)初始化器避免在每次調(diào)用方法時(shí)都會(huì)生成一些不必要的對(duì)象绸罗。

class Person {
    static {
        // 初始化整個(gè)類(lèi)需要用到的不可變可重用對(duì)象
    }
    public boolean isBaby() {
        // 這里使用到一些不可變的對(duì)象,無(wú)需每次都創(chuàng)建豆瘫,把創(chuàng)建操作放到靜態(tài)初始化器中珊蟀,這里直接使用即可
    }
}

缺點(diǎn):如果方法沒(méi)有被調(diào)用,那么初始化工作就沒(méi)有必要外驱,可以通過(guò)延遲初始化育灸,即把初始化工作放到方法初次調(diào)用時(shí)。

舉例四

自動(dòng)裝箱會(huì)創(chuàng)建出多余的對(duì)象昵宇。

sum 聲明為 Long 類(lèi)型磅崭,導(dǎo)致循環(huán)內(nèi)部構(gòu)造大量大于 Long 實(shí)例。

Long sum = 0;
for (long i = 0;i < Interger.MAX_VALUE; i ++) {
    sum += i;
}

要優(yōu)先使用基本類(lèi)型而不是裝箱基本類(lèi)型趟薄,要當(dāng)心無(wú)意識(shí)的自動(dòng)裝箱绽诚。

舉例五

通過(guò)維護(hù)自己的對(duì)象池來(lái)避免創(chuàng)建對(duì)象并不會(huì)一種好的做法,除非池中的對(duì)象是非常重量級(jí)的,現(xiàn)代JVM實(shí)現(xiàn)具有高度優(yōu)化的垃圾回收器恩够,其性能很容易就超過(guò)輕量級(jí)對(duì)象池的性能卒落。

6.消除過(guò)期的對(duì)象引用

  • 只要類(lèi)自己管理內(nèi)存,程序就應(yīng)該警惕內(nèi)存泄漏問(wèn)題
  • 內(nèi)存泄漏另一個(gè)場(chǎng)景來(lái)源是緩存
  • 另一個(gè)場(chǎng)景是監(jiān)聽(tīng)器和其他回調(diào)蜂桶,如果注冊(cè)了回調(diào)儡毕,卻沒(méi)有顯式地取消注冊(cè),那么會(huì)產(chǎn)生內(nèi)存泄漏扑媚,確毖澹回調(diào)立即被當(dāng)做垃圾回收的最佳方法是只保持它們的弱引用。

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

  • 介紹
    • 終結(jié)方法(finalizer)通常是不可預(yù)測(cè)的疆股,也是很危險(xiǎn)的费坊,一般情況下是不必要的
    • 使用終結(jié)方法導(dǎo)致行為不穩(wěn)定,降低性能旬痹,以及可移植性問(wèn)題
    • C++的析構(gòu)函數(shù)可以被用來(lái)回收其他的非內(nèi)存資源附井,Java 中,一般用try-finally塊來(lái)完成類(lèi)似工作
    • 終結(jié)方法的缺點(diǎn)在于不能保證被及時(shí)執(zhí)行两残,JVM會(huì)延遲執(zhí)行終結(jié)方法永毅,所以不要用來(lái)關(guān)閉已經(jīng)打開(kāi)的文件,程序不能依賴終結(jié)方法被執(zhí)行的時(shí)間點(diǎn)
    • 不應(yīng)該依賴終結(jié)方法來(lái)更新重要的持久狀態(tài)
    • System.gc 和 System.runFinalization 增加了終結(jié)方法執(zhí)行的機(jī)會(huì)人弓,但不能保證終結(jié)方法一定會(huì)被執(zhí)行
    • System.runFinalizersOnExit 可以保證終結(jié)方法被執(zhí)行沼死,當(dāng)然此方法已被廢棄
    • 如果被捕獲的異常在終結(jié)方法中被拋出,那么這種異常會(huì)被忽略
    • 使用終結(jié)方法有非常嚴(yán)重的性能損失崔赌,即使什么也不做
  • 終止資源(文件或線程資源)
    • 顯示提供一個(gè)終止方法意蛀,在實(shí)例不再需要時(shí),調(diào)用此方法
    • 通常與try-finally結(jié)構(gòu)結(jié)合峰鄙,在finally中顯式調(diào)用終止方法
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末浸间,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吟榴,更是在濱河造成了極大的恐慌魁蒜,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吩翻,死亡現(xiàn)場(chǎng)離奇詭異兜看,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)狭瞎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門(mén)细移,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人熊锭,你說(shuō)我怎么就攤上這事弧轧⊙┙模” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵精绎,是天一觀的道長(zhǎng)速缨。 經(jīng)常有香客問(wèn)我,道長(zhǎng)代乃,這世上最難降的妖魔是什么旬牲? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮搁吓,結(jié)果婚禮上原茅,老公的妹妹穿的比我還像新娘。我一直安慰自己堕仔,他們只是感情好擂橘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著贮预,像睡著了一般贝室。 火紅的嫁衣襯著肌膚如雪契讲。 梳的紋絲不亂的頭發(fā)上仿吞,一...
    開(kāi)封第一講書(shū)人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音捡偏,去河邊找鬼唤冈。 笑死,一個(gè)胖子當(dāng)著我的面吹牛银伟,可吹牛的內(nèi)容都是我干的你虹。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼彤避,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼傅物!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起琉预,我...
    開(kāi)封第一講書(shū)人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤董饰,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后圆米,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體卒暂,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年娄帖,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了也祠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡近速,死狀恐怖诈嘿,靈堂內(nèi)的尸體忽然破棺而出堪旧,到底是詐尸還是另有隱情,我是刑警寧澤奖亚,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布崎场,位于F島的核電站,受9級(jí)特大地震影響遂蛀,放射性物質(zhì)發(fā)生泄漏谭跨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一李滴、第九天 我趴在偏房一處隱蔽的房頂上張望螃宙。 院中可真熱鬧,春花似錦所坯、人聲如沸谆扎。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)堂湖。三九已至,卻和暖如春状土,著一層夾襖步出監(jiān)牢的瞬間无蜂,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工蒙谓, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斥季,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓累驮,卻偏偏與公主長(zhǎng)得像酣倾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谤专,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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

  • Effective Java筆記一 創(chuàng)建和銷(xiāo)毀對(duì)象 第1條 考慮用靜態(tài)工廠方法代替構(gòu)造器 第2條 遇到多個(gè)構(gòu)造器參...
    圣騎士wind閱讀 384評(píng)論 0 2
  • 目錄 第二章 創(chuàng)建和銷(xiāo)毀對(duì)象 1 考慮用靜態(tài)工廠方法替代構(gòu)造器 對(duì)于代碼來(lái)說(shuō), 清晰和簡(jiǎn)潔是最重要的. 代碼應(yīng)該被...
    高廣超閱讀 1,443評(píng)論 0 12
  • 第一章:Java程序設(shè)計(jì)概述 Java和C++最大的不同在于Java采用的指針模型可以消除重寫(xiě)內(nèi)存和損壞數(shù)據(jù)的可能...
    loneyzhou閱讀 1,243評(píng)論 1 7
  • 感覺(jué)時(shí)間到了躁锡。 該去實(shí)現(xiàn)對(duì)自己的承諾了。 我不可能把所有東西都抓在手里的置侍。
    JabinW閱讀 222評(píng)論 0 0
  • 我有兩條內(nèi)褲一條紅色一條藍(lán)色穿著紅色的映之,藍(lán)色的休息穿著藍(lán)色的,紅色的休息沒(méi)有哪個(gè)更重要
    wuya閱讀 112評(píng)論 0 0