Effective Java 閱讀 (1-3)

Effective Java 閱讀 (1-3)

真正開始閱讀這本書之後,我才發現我對這門語言的認識是多麼膚淺。

這是這個系列的第一篇博客,寫於2016年8月28日21:31:25,今天我從快遞小哥手上接過這本書,突然就有一種神祕的感覺,就好像一隻迷途的羔羊找到了新的道路。

哈哈扯遠了,希望能夠用這一系列的博客記錄一下我閱讀EJ的心得和體會吧。

更新頻率:1-3次/周(1-3周/次hhhh)


第一條:考慮使用靜態工廠方法

你還在使用共有構造器麼?

什麼是靜態工廠方法?EJ上給出了一個簡單的示例:

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

通過調用Boolean類的這個靜態的方法,我們就可以根據輸入值去新建一個Boolean對象,這比之我們平常的方法:

Boolean b = new Boolean(true);

有什麼優越的地方呢?

  1. 這個方法是有名稱的!
    有名稱爲什麼就很有意義呢?
    在EJ上舉了這樣一個例子:
    比如BigInteger類的初始化時,我們會調用BigInteger(int, int,random) 這個方法,但是有的時候這個構造方法會返回一個素數(你可以看做是返回了一個炸彈),可是我們怎麼提醒調用者這個構造方法可能會有恙呢?如果通過調換參數位置去新建一個構造方法的話只會讓類的構造方法更加混亂。
    這個時候,我們提供這樣一個靜態方法BigInteger.MaybeBoom(int , int , random) 這樣既能提醒使用者可能構造的對象的情況,又能區分不同參數,兩全其美。
    當然,你應該慎重地爲靜態工廠方法取名

  2. 你不在需要每次調用這個方法時都去新建一個對象啦!
    這種做法對於一個要頻繁創建相同對象的程序來說,可以極大的提高性能。它使得一個類可以保證是一個singleton;他使非可變類可以保證“不會有兩個相等的實例存在”。

  3. 他們可以返回你想要的任何子類型對象給你!
    使用靜態工廠方法,可以通過調用方法時使用不同的參數創建不同類的實例,還可以創建非公有類的對象,這就封裝了類的實現細節。
    也就是API可以返回對象,同時又不會令對象的類變成共有的,這樣隱藏的實現類會使得API變得非常簡潔。

    遇到多個構造器參數時要考慮使用構建器

    比如說:你有一個類Point

public class Point{
    private double [] coor ;//必須的
    private String poiid ; //有的點可沒有這個信息
    private String status ;//有的點可沒有這個信息
    private String checkinNum ;//有的點可沒有這個信息
    ... 
}

當你在寫這個Point的構造函數的時候,若這個Point只有coor[]這個參數是必須的,那麼你就可以用以下這幾種解決方法。

    1. 重疊構造器
...
public Point(double coor[]){
    Point(double[] coor,"no poiid");
}
public Point(double coor[] , String poiid){
    Point(double[] coor , String poiid ,"no status info")
}
public Point(double coor[] , String poiid , String status){
    Point(double[] coor , String poiid ,status , 0);
}
public Point(double coor[] , String poiid , String status , int checkinnum){
    this.coor = coor ;
    this.poiid = poiid ;
    this.status = status ;
    this.checkin = checkinnum ;
}
...

當你對於一個點只知道其某個必要訊息,但是你又需要去實例化這個點,就利用參數列表中相對應的構造器,但是構造器的調用過程中會包含所有的參數

但是如果僅僅是這幾個參數,感覺程序並不臃腫,但是隨着參數的增加,這種構造器的實現方法就會越來越複雜。

    1. JavaBean
public Point(){
    public void setCoor(double[] val){ coor = val ;}
    public void setPoiid(String val){ poiid = val ;}
    public void setStatus(String val){ status = val ;}
    public void setCheckin(int val){ checkin = val ;}   
}

使用JavaBean能夠彌補使用重疊構造器的不足,他可以很容易的創建一個對象。

但是問題也來了,由於JavaBean模式在使用中構造過程被分配到好幾個不同的調用中,所以在調用過程中可能會處在一個不一致的情況下!類無法通過檢驗構造器的參數的有效性來保證一致性。

同時JavaBean阻止了把類做成不可變的可能,所以線程安全變成了很大的問題!

    1. 使用Builder模式

什麼是Builder模式?就是把JavaBean再次深化,我們先去創建一個Builder,這個Builder中包含了所有我們想要對我們將要進行定義的類的值的定義。然後我們使用這個Builder去定義我們將要定義的類。這樣就保證了我們想要定義的類的一致性。

public class Point{
    private double [] coor ;//必須的
    private String poiid ; //有的點可沒有這個信息
    private String status ;//有的點可沒有這個信息
    private String checkinNum ;//有的點可沒有這個信息
    public Point(){
        coor = builder.coor ;
        poiid = builder.poiid ;
        status = builder.status ; 
        checkinnum = builder.checkinnum ;
    }
    public static class Builder{
        private double [] coor ;//必須的就不初始化
        private String poiid = "no poiid"; //有的點可沒有這個信息
        private String status = "no status";//有的點可沒有這個信息
        private String checkinNum = 0;//有的點可沒有這個信息

        public builder(double[] coor){//初始化必要的變量
            this.coor = coor;
        }
        public Builder poiid(String val){ 
            poiid = val ; 
            return this ;
        }
        public Builder status(String val){ 
            status = val ;
            return this ;
        }
        public Builder checkin(int val){ 
            checkin = val ;
            return this ;
        }   
    }   
}

比如新建一個Point類方法

Point p = new Point.Builder(coor).poiid(poiid).
status(status).checkin(check);

附上一個Builder的泛型

public interface Builder<T>{
    public T build();
}

builder的缺陷:
* 爲了創建對象,還需要創建其構建器。雖然說創建構建器的開銷在實踐中可能不明顯,但是爲了性能,還真是個問題~
* Builder模式比較冗長,故最好只在參數很多的時候使用

使用私有的構造器或者枚舉類強化Singleton屬性

Singleton就是隻被實例化一次的類,也就是單例模式,比如說唯一的系統組件。

在Java 1.5之前的兩種構造Singleton的方法
A:

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

    public void leaveTheBuilding(){...}
}

即私有的構造器,私有的構造器只能在類內部被調用一次,用來創建final域的single.INSTANCE,由於缺少public或者protected的構造器,所以能夠保證single的全局唯一性,一旦single被實例化,只會產生一個single實例,任何行爲都不會改變這一點。

不過享有特權的客戶端可以藉助AccessibleObject.setAccessible方法,通過反射機制調用私有構造器。如果要徹底地域這種攻擊,可以修改構造器,讓它在第二次創建的時候拋出異常(比如內定個final flag進行判斷)。

B:

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

    public void leaveTheBuilding(){...}
}

第二種方法,共有的成員變量是一個靜態工廠方法。對於所有的getInstance方法,都會返回同一個對象引用,所以,永遠都不會創建其他的single實例(同之前的提醒哦)

這個方法的好處在於,組成類的成員的聲明可以很清楚地表明這個類是一個Singleton。
這個方法的優勢在於,它提供了靈活性,即在不改變API的前提下,我們可以輕鬆改變這個類是否爲Singleton。工廠方法返回該類的唯一實例,但是它可以很容易被修改,比如改成每個調用該方法的線程返回一個唯一的實例。

Java1.5之後還有第三種方法去實現Singleton,只需要一個包含單個元素的枚舉類

public enum single{
    INSTANCE ;

    public void leaveTheBuilding(){...}
}

這個方法更加簡潔,無償提供了序列化機制(這個我還不是很理解,所以之前的還沒有寫這個序列化)。雖然這種方法還沒有廣泛採用,但是單元素的枚舉類型已經成爲實現Singleton的最佳方法!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章