這裏是閱讀《Effective Java中文版第二版》的讀書筆記,這裏會記錄一些我的感受稍微有些重要的內容,方便之後查閱,可能會由於我的實力緣由致使理解有誤,如有發現歡迎指出。一些我的還不理解的會用斜線標註。java
第一章是引言,因此跳過。設計模式
靜態工廠方法是指一個返回類的實例的靜態方法,例如:數組
public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean,FALSE; }
衆所周知,構造器的方法名是必須和類名同樣的,所以對於有多個參數類型相同的構造方法,一種方法是更改參數的順序,另外一種是增長一個flag來判斷執行哪一個構造方法。可是這樣對於使用者是不友好的,他必須熟悉API或者查閱開發文檔。假若使用靜態工廠方法,那麼能夠經過方法名來給予使用者良好的提示與說明。框架
這句話的典型應用是在設計模式的單例模式中,靜態工廠方法可以爲重複的調用返回相同的對象。函數
構造方法是不能使用return語句的,它在使用時也只能產生自身這個類的一個對象,而靜態工廠方法可使用return語句,所以在選擇返回對象時就有了更大的靈活性。這個優點的應用不少,好比服務提供者框架模式。工具
應當熟悉靜態工廠方法和構造器的各自的長處,在合適的場景使用合適的方法。性能
在面對一個擁有多個屬性的類且構造方法擁有多個可選參數時,一個常見的方法是使用重疊構造器模式(建立多個構造方法,每一個構造方法比前一個構造方法有新的參數)。例如,第一個構造方法有兩個必須參數,第二個構造方法有兩個必須參數和一個可選參數,第三個構造方法有兩個必須參數和兩個可選參數,以此類推。可是當有許多參數的時候,代碼會變得很難編寫,也很難閱讀,甚至會容易出錯。測試
另外一個方法是使用javabean模式。由於構造過程被分到了多個調用中(爲每一個屬性的賦值調用該屬性的set方法),在構造過程當中,javabean可能處於不一致的狀態,這種問題難以發現。ui
第三種方法就是構建器模式(Builder模式)的一種形式。this
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 { // 必須屬性 private final int servingSize; private final int servings; // 可選屬性 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 setCalories(int calories) { this.calories = calories; return this; } public Builder setFat(int fat) { this.fat = fat; return this; } public Builder setSodium(int sodium) { this.sodium = sodium; return this; } public Builder setCarbohydrate(int carbohydrate) { this.carbohydrate = carbohydrate; 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; } } // 使用方法 NutritionFacts n = new NutritionFacts.Builder(200,10).setCalories(20).setFat(30).build();
Builder模式十分靈活,能夠利用一個builder來建立多個相同的對象,而且對必須參數和可變參數的實現符合人類的正常思惟。另外,對於使用者而言,使用時的代碼更容易閱讀和編寫。
這種方法我在google的protobuf的java實現中見到過。
私有構造方法就不提了,這裏記錄一下第二個:
public enum A { INSTANCE; public void leaveTheBuilding() {...} }
對於一些只包含靜態方法或者靜態屬性的類(好比工具類),咱們不但願他們被實例化。衆所周知,在缺乏顯式構造方法的時候,編譯器會默認添加一個無參的構造方法。若是爲了嚴謹,咱們能夠添加一個私有的構造方法,更能夠在這個構造方法中throw異常來停止程序。
通常來講,最好能重用對象而不是在每次須要的時候就建立一個相同功能的新對象。
除了重用不可變的對象以外,也能夠重用那些已知不會被修改的可變對象。
能使用基本數據類型,就儘可能不要用對應的封裝類。
不能覺得有了垃圾回收機制後,就不須要考慮內存管理的事情了。
例如用數組來實現棧,當實現出棧操做,size-1後,棧頂座標後的元素對使用者來講就已是無效部分了,可是數組仍然擁有對它們的引用,所以垃圾回收機制不會將它們回收。解決辦法是在出棧時,將引用置空。
除了特定狀況,不要使用終結方法(finalize)。
子類覆蓋了父類的終結方法後,子類的終結方法不會自動調用父類的終結方法,須要手動調用。
約定的內容:
equals方法實現了等價關係。
實現高質量equals方法的訣竅:
一個簡單的列子:
public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof MyClass)) return false; MyClass obj = (MyClass) o; return obj.field0 == this.field0 && obj.field1 == this.field1; }
告誡:
public boolean equals(MyClass o); // Don't do this!
若是沒有共同覆蓋equals方法和hashCode方法,那麼該類將沒法結合全部基於散列的集合一塊兒正常運做,這樣的集合包括HashMap、HashSet和HashTable。
約定:相等的對象必須具備相等的散列碼(HashCode)。
在散列碼的計算過程當中,必須排除equals比較計算中沒有用到的任何字段,能夠把冗餘字段(它的值能夠根據參與計算的其餘字段計算出來)排除在外。
不要試圖從散列碼計算中排除掉一個對象的關鍵部分來提升性能。
提供好的toString實現可使類用起來更加溫馨。
若是你繼承了一個實現了Cloneable接口的類,那麼你除了實現一個行爲良好的clone方法外,沒有別的選擇。不然,最好提供某些其餘的途徑來代替對象拷貝,或者乾脆不提供這樣的功能。
另外一個實現對象拷貝的好方法是提供一個拷貝構造方法或者拷貝工廠。
// 拷貝構造方法 public MyClass(MyClass mc); // 拷貝工廠 public static MyClass newInstance(MyClass mc);
類實現了Comparable接口,就代表它的實例具備天然順序關係(natural ordering)。
約定:(符號sgn(表達式)表示數學中的signum函數,根據表達式的值爲負值、零和正值,分別返回-一、0和1)
sgn(x.compareTo(y)) == -sgn(y.compareTo(x))
。(這也意味着,當且僅當y.compareTo(x)拋出異常時,x.compareTo(y)才必須拋出異常)x.compareTo(y) > 0 && y.compareTo(z) > 0
成立意味着x.compareTo(z) > 0
。x.compareTo(y) == 0
意味着全部的z都知足sgn(x.compareTo(z)) == sgn(y.compareTo(z))
。(x.compareTo(y) == 0) == (x.equals(y))
,但這絕非必要。若違反了這個條件,應當給予說明。比較浮點字段用Double.compare或者Float.compare。
若是一個類有多個關鍵字段,按照什麼樣的順序來比較是很是重要的。
compareTo方法中,若是兩個對應字段不相等,可使用該類的字段與傳入參數的字段的差值做爲返回值,但應確保差值是絕對正確的。