博主雙12入手了一本"Effective Java第二版",本系列文章將初步梳理書中內容,我也查了些資料,我會針對知識點做一點展開,方便以后復習回顧;
Item1.考慮用靜態工廠代替構造器:
靜態工廠的優勢:
更易於閱讀(有名稱) |
可以做成單例(Singleton)的 |
可以實現多態(返回多個子類型的對象) |
在創建參數化類型的實例時,他們使代碼變得更加簡潔 |
靜態工廠存在的不足:
1.類如果不含共有的或者受保護的構造器,就不能被子類化;
2.他們與其它的靜態方法其實沒區別,因此對於客戶來說如何使用變成了難題.
Item2:對於多個構造器參數時,利用構建器(Builder Mode):
利用構造器重載確實可以解決少量構造器參數對象的初始化,但大量比如十幾個甚至幾十個的時候,客戶端的代碼很難編寫,更別說閱讀了,建議用建造者模式來解決;
建造者模式的核心就是:將需要的域(包括必須的參數和不必須的參數)放在一個Builder類中,必須的參數放在Builder的構造器中初始化(只要用到Builder對象就會調用其構造器),可選參數利用對應的set方法供客戶端調用實現初始化:
//Builder Parttern public class User { private final String userName; private final String pwd; private final String mobile; private final String e_mail; public static class Builder { //Required parameters private final String userName; private final String pwd; //Optional parameters - initialized to default values private final String mobile = "13900000000"; private final String e_mail = "1@1.com"; public Builder(String userName, String pwd){ this.userName = userName; this.pwd = pwd; } public Builder mobile(String mobile){ this.mobile = mobile; return this; } public Builder e_mail(String e_mail){ this.e_mail = e_mail; return this; } public User build(){ return new User(this); } } private User(Builder builder){ userName = builder.userName; pwd = builder.pwd; mobile = builder.mobile; e_mail = builder.e_mail; } } //Client Code User user = new User.Builder("Joey","123").mobile("13512511111"). e_mail("joey@gmail.com");
但建造者模式實現也不是完美的,為了創建對象,必須先創建其構建器,在十分注重性能的場景下,它有可能比重載構造器更加冗長,如果要用建造者模式,最好一開始就用(不要和構造器或靜態工廠混用).
Item3:用私有構造器或者枚舉類型強化Singleton屬性:
我們平常構造Singleton的時候,一般先私有化構造器,再利用靜態工廠控制對象,但享有特權的客戶端可以借助AccessibleObject.setAccessible方法,通過反射機制調用私有構造器,如果要抵御這種攻擊,可以改造構造器,讓構造器創建第二個實例的時候拋出異常;
當然有更好的方法,只需編寫一個包含單個元素的枚舉類型:
public enum Singleton { INSTANCE; public void leaveTheBuilding(){...} }
此方法既可以完美地防止了多次實例化,並無償提供了序列化機制,即便面對復雜序列化或反射攻擊的時候.....
Item4:避免創建不必要的對象:
此條目涵蓋的內容比較多,中心點就是能重用(immutable)的對象就不要去創建一個相同功能的對象.下面有些例子:
1.盡量少用new String(),推薦把字符串放入常量池:String str = "abc";
2.方法中實例化Calendar類的對象,若多次調用,會產生多個相同功能的對象,解決辦法:寫一個靜態代碼塊,可重用的對象或者變量都可以放進去;
3.適配器(adapter)模式中,有些特定的適配器不需要多個適配器實例,例如:Map接口的keySet方法返回Map中key的Set,感覺上,每次調用keySet都創建了一個新的實例,實際上用的是同一個實例,因為此實例的作用是一樣的,雖然多個實例是沒有害處的,同樣,沒有必要這么做;
4.再Java1.5的發行版本中,擁有自動裝箱(autoboxing)的新特性,觀察以下代碼:
public static coid main(String []args){ Long sum = 0L; for(long i = 0; i < Integer.MAX_VALUE; i++){ sum += i; //無意識的自動裝箱 } System.out.println(sum); }
自動裝箱的過程中會自動創建包裝類的實例,如果執行完上面的代碼,JVM會多構造了大約231個實例,如果將sum定義為基本數據類型long,性能將會提高幾倍..
當然並不意味着我們必須盡可能地避免創建對象,通過創建代價不大的小對象來提升程序的清晰性和間接性是非常好的.
總結一句就是:當你應該重用對象的時候,請不要創建新的對象;當你應該創建新對象的時候,請不要重用對象..