《effective java》讀書筆記1(建立和銷燬物件)

狂奔的CD發表於2018-02-10

第1條:考慮用靜態工廠方法代替構造器

1.what is?
此處的靜態工廠方法與設計模式中的工廠模式不一樣。

比如類

class Person{

    //A的構造器
        public A(){};

    //A的靜態工廠方法可以是
        static Person  Male;       //男人集合
        static Person  Female;   //女人集合
    public static getMale(){
        return Person.Male;
    }
}

2.why?
》第一個優勢:它們有直接的名稱。而過載的構造器沒有名稱,沒有說明的情況下可能很難辨認。
》第二個優勢:不必每次都建立新物件。通過構造器每次都new出一個物件,而像上例的getMale則可以重用一個物件。
》第三個優勢:可以返回任何子類的物件,同時又不會讓物件的類變成公有的,更加靈活,api更加簡潔。同時在編寫該靜態工廠方法時,子類可以不必存在。

e.g.:服務提供者框架。其中有三個重要的元件:服務介面,服務提供者註冊API(註冊後客戶端可訪問該服務),服務訪問API(客戶端獲取服務例項)。第四個可選元件是服務提供者介面(用於建立其服務實現的例項),如果沒有,就按照類名註冊,並通過反射方式例項化。

》第四個優勢:建立引數化型別例項的時候,它們使程式碼變得更加簡潔。

e.g.:Map<String,List<String>> m = new HashMap<String,List<String>>();
可以使用靜態工廠方法進行型別推導
public static <K,V> HashMap<K,V> newInstance(){ return new HashMap<K,V> ();}

貌似jdk1.7裡面就是這麼幹的。

3.but
》第一個缺陷:類如果不含public或者protected的構造器,就不能被子類化
》第二個缺陷:與其他靜態方法沒有區別,不好辨認。

第2條:遇到多個構造器引數時要考慮用構建器

1.what?
當構造器有n個引數時程式設計師習慣用重疊構造器

class A{
    public A(){this.A(null)}
    public A(B){
        this.A(B,null)
    }
    public A(B,C){
        this.A(B,C,null)
    }
    public A(B,C,D)
    ….
}

很淡疼對不對?

替代品有JavaBeans:

class A{
    B,C,D;
    public A(){}
        //getter and setter
}

但是這樣分成多個步驟建立例項,會產生多執行緒下不安全的問題,必須保證執行緒安全的情況下再這麼做。

於是第三種替代品出現,Builder模式

class A{
    B,C,D;
    static class Builder{
        B,C,D;
        public Builder(){}
        public Builder setB(B){
            this.B = B;
            return this;
        }
        public Builder setC(C){
            this.C = C;
            return this;
        }
        public Builder setD(D){
            this.D = D;
            return this;
        }
        public A build(){
            return new A(this);
        }
    }

    private A(Builder b){
        this.B = b.B;
        this.C = b.C;
        this.D = b.D;
    }
}

相信都見過,這種叫做構建器。在build或者setter方法中對引數做強約束檢查。

缺陷:需要先new構建器,引數設定也冗長,如果引數較少(3個以內)可以不用。

總之,如果類的構造器或者靜態工廠中具有多個引數時,Builder模式是種不錯的選擇。

第3條:用私有構造器或者列舉型別強化Singleton屬性,即單例

單例大家都很熟悉,用的多。比如

class A{
    private final static A INSTANCE = new A();
    private A(){}
    public void getInstance(){
        return INSTANCE;
    }
}

但是通過反射機制依然是可以訪問私有屬性的。而且如果A需要序列化,加上implements Serrializable是不夠的,為了保證單例,必須宣告所有例項域都是transient的,並提供readResolve方法,否則每次反序列化都會建立一個新例項。

private Object readResolve(){
    return INSTANCE;
}

從jdk1.5開始,實現單例還有第三種方法,利用包含單個元素的列舉型別:

public enum A{
    INSTANCE;
}

第4條:通過私有構造器強化不可例項化的能力

第5條:避免建立不必要的物件。避免無意識的自動裝箱。小物件的建立銷燬很廉價,不要用物件池維護,消耗大的比如資料庫會採用連線池。

第6條:消除過期的物件引用

》情況一:Stack類自己管理記憶體,對於pop出的資料自己認為是過期的,但是對於垃圾回收器來說並不清楚,所以pop的時候主動釋放才能避免記憶體洩漏。類似這種情況,只要類是自己管理記憶體,就應該警惕記憶體洩漏問題。

》情況二:快取中的物件引用
》情況三:監聽器和其他回撥。
(ps:以上情況我在安卓開發中親身經歷過無數遍)

第7條:避免使用終結方法finalize

1)終結方法不一定及時執行,且有效能損失。

2)替代方案是顯式定義一個的終止方法,並要求該類的每個例項不再有用時呼叫該方法。如果該類已經被終止,再次呼叫應該丟擲IllegalStateException
(ps:這個也是,安卓開發中有時候activity無法釋放,需要自己手動控制釋放)

最優設計:
try{}catch(){}finally{ 自定義的終止方法 },java的io流都有自己的終止方法。

3)
》終結方法的好處一:當你忘了呼叫自己的終止方法時,終結方法可以充當安全網,遲到總比不到好。如果到了這一步應該加個錯誤日誌,這是一個bug
》終結方法的第二個合理用途:當java物件通過native方法委託給一個本地物件時,稱為本地對等體,很明顯java的垃圾回收器是不知道它的,java物件被回收時這個本地物件還在蹦噠。當它不具有關鍵資源的情況下,終結方法就是最合適的。如果具有關鍵資源,就需要顯示的終止方法。

4)終結方法鏈不會自動執行,應該在try塊中終結子類,然後finally塊中呼叫超類的終結方法。

5)總之,除非作為安全網,或者是終止非關鍵的資源,否則不要使用終結方法。

相關文章