创建和销毁对象
考慮使用靜態工廠方法代替構造器
類可以提供一個公有的靜態工廠方法(public static factory method)來返回一個類的實例。
例如,Boolean類的valueOf()方法:
提供public靜態工廠方法而不是public的構造器的優勢如下:
靜態工廠方法有名稱,不過要慎重地選擇名稱
例如,構造器BigIntger(int, int, Random)返回的BigInteger可能為素數,如果用名為BigInteger.probablePrime()的靜態工廠方法顯然更清晰。?
示例:?BigIntegerTest
不必在每次調用靜態工廠方法的時候都創建一個新對象?
如果程序經常經常請求創建相同的對象,并且創建對象的代碼很高,則這項技術可以極大地提升性能。?
示例:BooleanTest?
當且僅當a==b的時候才有a.equals(b)為true, 如果類保證了這一點, 它的客戶端就可以使用==操作符來代替equals(Object)方法,這樣可以提升性能。
靜態工廠方法可以返回子類類型的對象?
API返回的對象對應的子類可以不是公有的,這種方式可以隱藏具體的實現類。?
這項技術適合于基于接口的框架,接口為靜態工廠提供了自然返回類型。?
接口不能有靜態方法,因此按照慣例,返回接口的靜態工廠方法是放在一個不可實例化(non-instantiability)的類中。?
示例:?InterfaceCannotStaticTest?
示例: java.util.Collections是一個不可實例化的類,它提供了不可修改的集合、同步集合的靜態工廠方法。我們模仿其寫了一個簡單例子:?MyListTest?
通過使用這種靜態工廠方法,甚至要求客戶端通過接口來引用被返回的對象,而不是通過它的實現類來引用被返回的對象,這是一種良好的習慣。 為了提升軟件的可維護性和性能,返回對象的(子)類也可能隨著發行版本的不同而不同。?
示例:EnumSetTest?發行版本1.5中引入java.util.EnumSet沒有公有的構造器,只有靜態工廠方法,返回兩種實現類之一,具體則取決于底層枚舉類型的大小。
示例:服務提供者框架
靜態工廠方法在創建參數化類型實例的時候,使得代碼更加簡潔?
例如,創建一個Map<String, List<String>>對象,在調用參數化類的構造器時,即使類型參數明顯,也必須指明。這通常要求你連接兩次提供類型參數:
Map<String, List<String>> map = new HashMap<String, List<String>>();?
隨著類型參數變得越來越長,越來越復雜,這一冗長的說明也很快變得痛苦起來。?
但是有了靜態方法,編譯器可以替你找到類型參數,這被稱作類型推導(type inference):?
Map<String, List<String>> map = HashMap.newInstance();?
假設HashMap提供了這個靜態工廠方法:
public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>(); }?
總有一天,Java將能夠在構造器調用以及方法調用中執行這些類型推導,但到發行版本1.6為止暫時還無法這么做。?
遺憾的是,到發行版本1.6為止,標準的集合實現如HashMap并沒有工廠方法,但是可以把這些方法放在你自己的工具類中。
公有的靜態工廠方法的缺點:
| valueOf | Returns an instance that has, loosely speaking, the same value as its parameters. Such static factories are effectively type-conversion methods. |
| of | A concise alternative to valueOf , popularized by EnumSet. |
| getInstance | Returns an instance that is described by the parameters but cannot be said to have the same value. In the case of a singleton, getInstance takes no parameters and returns the sole instance. |
| newInstance | Like getInstance , except that newInstance guarantees that each instance returned is distinct from all others. |
| getType | Like getInstance , but used when the factory method is in a different class. Type indicates the type of object returned by the factory method. |
| newType | Like newInstance , but used when the factory method is in a different class. Type indicates the type of object returned by the factory method. |
簡而言之,靜態工廠方法和公有構造器都各有用處。 靜態工廠方法通常更加適合,因此切忌第一反應是提供公有的構造器,而不先考慮靜態工廠方法。
遇到多個構造器參數時要考慮用構建器
靜態工廠和構造器有一個共同的局限性:它們都不能很好地擴展到大量的可選參數。考慮用一個類表示一個食品的營養成分表。對于這樣的類,應該用哪個構造器或者靜態方法來編寫呢?
示例: telescoping_constructor/NutritionFacts?
① 當有許多參數的時候,客戶端代碼會很難編寫,并且仍然較難以閱讀。?
② 如果讀者想知道那些值是什么意思,必須很仔細地數著這些參數來探個究竟。如果客戶端不小心顛倒了其中兩個參數的順序,編譯器也不會出錯,但是在程序運行時會出現錯誤行為。
示例: javabeans/NutritionFacts?
① 無法保證一致性:JavaBean模式自身有著很嚴重的缺點,因為構造過程被分到了幾個調用中,在構造過程中JavaBean可能處在不一致的狀態。類無法僅僅通過檢驗構造器參數的有效性來保證一致性。試圖使用處于不一致狀態的對象,將會導致失敗,這種失敗調試起來十分困難。?
② 阻止了把類做成不可變,這就需要程序員付出額外的努力來確保它的線程安全。
示例:builder/NutritionFacts?
這樣的客戶端代碼很容易編寫,而且易讀。Builder模式模擬了具名的可選參數,就像Python一樣。?
對參數加強約束條件: build()方法檢驗或者多個setter()方法檢驗這些約束條件,如果該約束條件沒有得到滿足,就拋出IllegalStateException或IllegalArgumentException。?
設置了參數的Builder生成了一個很好的抽象工廠(Abstract Factory)。Java中的Class對象就是一個抽象工廠,用newInstance()方法充當build()方法的一部分。?
Builder模式也有它自身不足,為了創建對象,必須先創建它的構建器,它還會比重疊構造器模式更加冗長,因此只有在很多參數(4個以上)的時候才考慮使用Builder模式。但是要記住,將來你可能需要添加參數,如果一開始就使用構造器或者靜態工廠,等到類需要多個參數時才添加構建器,就無法控制,那些過時的構造器或者靜態工廠顯得十分不協調。因此通常最好一開始就使用構建器。
用私有構造器或者枚舉類型強化Singleton屬性
Singleton指僅僅被實例化一次的類。Singleton通常被用來代表那些本質上唯一的系統組件。?
在Java 1.5發行版本之前,實現Singleton有兩種方法。這兩種方法都要把構造器保持為私有的,并導出公有的靜態成員,以便允許客戶端能夠訪問該類的唯一實例。?
第一種方法,公有靜態成員是一個final域。示例: field/Elvis.java?
私有構造器僅被調用一次,用來實例化公有的靜態域Elvis.INSTANCE。?
但要提醒一點:享有特權的客戶端可以借助AccessibleObject.setAccessible()方法,通過反射機制調用私有構造器,示例:ModifyingSingleton?。如果需要抵御這種攻擊,可以修改構造器,讓它在被要求創建第二個實例的時候拋出異常。
通過私有構造器強化不可實例化的能力
避免創建不必要的對象
消除過期的對象引用
避免使用終結方法
參考資料
- 《Effective Java》第2版
- Java反射AccessibleObject類的setAccessible方法
轉載于:https://www.cnblogs.com/fireway/p/7266588.html
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: PHP中header函数的用法及其注意重
- 下一篇: 13_观察者模式