Effective Java 3

《Effective Java》做者是美國的Joshua Bloch,連Gosling都說須要看的書,討論的是更深層次的Java開發技術,適合於有必定Java基礎的人看。html

這是一本分享經驗於指引您少走彎路的經典著做,針對如何編寫高效、設計優良的程序提出了最實用的方針。程序員

 

 

Item1 考慮用靜態工廠方法代替構造器數組

一、優勢緩存

可讀性強。安全

不會每次調用就經過構造器建立一個新的實例。工具

能夠返回原始類型的任何子類型。性能

二、缺點ui

只有私有構造器沒法被子類化。this

 

Item 2 遇到多個構造器參數考慮用構建器Builderspa

 

一、傳統的重疊構造器模式

 

public class NutritionFacts {
    private final int servingSize;  // (mL)            required
    private final int servings;     // (per container) required
    private final int calories;     // (per serving)   optional
    private final int fat;          // (g/serving)     optional
    private final int sodium;       // (mg/serving)    optional
    private final int carbohydrate; // (g/serving)     optional

    public NutritionFacts(int servingSize, int servings) {
        this(servingSize, servings, 0);
    }

    public NutritionFacts(int servingSize, int servings,
                          int calories) {
        this(servingSize, servings, calories, 0);
    }

    public NutritionFacts(int servingSize, int servings,
                          int calories, int fat) {
        this(servingSize, servings, calories, fat, 0);
    }

    public NutritionFacts(int servingSize, int servings,
                          int calories, int fat, int sodium) {
        this(servingSize, servings, calories, fat, sodium, 0);
    }
    public NutritionFacts(int servingSize, int servings,
                          int calories, int fat, int sodium, int carbohydrate) {
        this.servingSize  = servingSize;
        this.servings     = servings;
        this.calories     = calories;
        this.fat          = fat;
        this.sodium       = sodium;
        this.carbohydrate = carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola =
                new NutritionFacts(240, 8, 100, 0, 35, 27);
    }
    
}

  

若是讀者想了解那些值是什麼意思,必須很費勁的仔仔細細的數着這些參數來探究。若是客戶端不當心顛倒了這些參數的順序,編譯器也不會報錯,可是程序在運行時會出現錯誤行爲。

JavaBeans模式

public class NutritionFacts {
    // Parameters initialized to default values (if any)
    private int servingSize  = -1; // Required; no default value
    private int servings     = -1; // Required; no default value
    private int calories     = 0;
    private int fat          = 0;
    private int sodium       = 0;
    private int carbohydrate = 0;

    public NutritionFacts() { }
    // Setters
    public void setServingSize(int val)  { servingSize = val; }
    public void setServings(int val)     { servings = val; }
    public void setCalories(int val)     { calories = val; }
    public void setFat(int val)          { fat = val; }
    public void setSodium(int val)       { sodium = val; }
    public void setCarbohydrate(int val) { carbohydrate = val; }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts();
        cocaCola.setServingSize(240);
        cocaCola.setServings(8);
        cocaCola.setCalories(100);
        cocaCola.setSodium(35);
        cocaCola.setCarbohydrate(27);
    }
}

  

這樣實例建立很容易,客戶端代碼讀起來很清晰明朗,可是,程序員須要付出額外的努力來確保它的線程安全

Builder模式

public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int sodium        = 0;
        private int carbohydrate  = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
        { calories = val;      return this; }
        public Builder fat(int val)
        { fat = val;           return this; }
        public Builder sodium(int val)
        { sodium = val;        return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val;  return this; }

        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }

    public static void main(String[] args) {
        NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                .calories(100).sodium(35).carbohydrate(27).build();
    }
}

  

builder設置方法返回this,造成了鏈式調用,這個一個流式的API,客戶端代碼很容易編寫,更爲重要的是易於閱讀。

二、使用構建器的好處。

在多參數時寫法優雅,參數具備可讀性,保證線程安全,適合類的繼承。

三、使用構建器的壞處。

花費會更高,所以在參數有許多的時候建議使用,特別是有不少可選參數時。

 

Item 3 Singleton的最佳實現方式是枚舉類型

一、什麼是枚舉類

public class EnumTest {
    public static void main(String[] args) {
        Scanner in=new Scanner(System.in);
        System.out.println("put:");
        String input=in.next().toUpperCase();
        Size size=Enum.valueOf(Size.class,input);
        System.out.println(size);
        System.out.println(size.getAbbreviation());
        System.out.println(Size.SMALL.getAbbreviation());
    }

}
enum Size{
    SMALL("S"),MEDIUM("M"),LARGE("L");
    private String abbreviation;
    private Size(String abbreviation){this.abbreviation=abbreviation;}
    public  String getAbbreviation(){return abbreviation;}

}

  

二、優勢

提供序列化機制,甚至在反射機制下也能確保只有單例。

三、缺點

沒法繼承自除了Enum以外的超類以及其餘子類進行繼承。

 

Item 4 經過私有構造器強化不可實例化的能力

一、優勢

在須要建立相似工具類等不須要實例化的類時,將構造器私有化,以保證該類在任何狀況下都不會被實例化。

二、缺點

沒法被繼承

 

Item 5 使用依賴注入去鏈接資源

一、依賴注入

public class dependency {
	private final String str;

	public dependency(String str) {
		this.str = str;
	}
}

  

二、優勢

對於一個行爲由其餘資源所參數化所決定的類來講,使用靜態工具類或者單例是不適合的。而是應該當建立一個實例是將資源傳入構造器。

 

Item 6 避免建立沒必要要的對象

一、優勢

對於一些不會發生改變的變量或是常量,使用static塊進行初始化,使某些類不會被重複建立,減小開銷。例如 new String就是一個很差的習慣。

在構造器中使用靜態工廠就是個不錯的方法。重點是對靜態的使用static(final)。自動裝箱也極可能引發巨大的開銷。

 

Item 7消除過時的對象引用

一、優勢

例如對於Stack類中的數組當執行pop()操做後,被彈出的元素並不會自動被gc所回收。所以須要手動進行釋放。

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

  

當一個類本身進行內存的管理時,這種情況就尤爲要注意。

進行緩存時也須要注意這個問題。

對於監聽器以及回調操做,也須要注意。

 

Item 8 儘可能避免使用Finalizers

一、優勢

因爲Finalizers的優先級很低,不能保證Finalizers什麼時候會被調用,會致使內存開銷的增長,而且會大幅下降程序的性能。

永遠不要Finalizers來更新持久狀態。

對含有Finalizers方法的類進行子類化會有嚴重的安全隱患。

使用try-with-resources做爲某些資源的結束方法。而且對於其狀態是否被關閉須要在私有域中進行記錄。這樣其餘方法調用時能夠對狀態進行檢測。

Finalizers有兩種合理的用法:

一、在忘記調用close方法時做爲一張安全網,但這也只是一個不得以的備用措施,仍然會形成很大開銷,而且不知什麼時候會進行。

二、native peer (?)

 

Item 9 更偏向使用 try-with-resources 塊

一、try-with-resources

在try()中進行一個或多個的資源連接或讀取。而且這些資源是必須被關閉的的,使用這個語法將會被自動關閉無需顯示調用close方法。

在{}進行實際操做。

 一樣可使用catch語句

    static void copy(String src, String dst) throws IOException {
        try (InputStream   in = new FileInputStream(src);
             OutputStream out = new FileOutputStream(dst)) {
            byte[] buf = new byte[BUFFER_SIZE];
            int n;
            while ((n = in.read(buf)) >= 0)
                out.write(buf, 0, n);
        }
    }

  

二、優勢

能夠同時打開多個資源,並保證被關閉,而無需顯式調用close方法。

exception不會被覆蓋,能夠查看每一個exception.

 

 

參考:《Effective Java》第3版

參考:https://www.cnblogs.com/WutingjiaWill/p/9139520.html

相關文章
相關標籤/搜索