本章的主題是創(chuàng)建和銷毀對象:何時以及如何創(chuàng)建對象,何時以及如何避免創(chuàng)建對象奏候,如何確保它們能夠適時地銷毀循集,以及如何管理對象銷毀之前必須進行的各種清理動作。
注意:靜態(tài)工廠方法與設計模式中的工廠方法模式不同蔗草。本條目中所指的靜態(tài)工廠方法并不直接對應設計模式中的工廠方法咒彤。
[toc]
類可以通過靜態(tài)工廠方法來提供它的客戶端,而不是通過構造器咒精。這么做有以下幾大優(yōu)勢:
它們有名稱镶柱。
當一個類需要多個帶有相同簽名的構造器時,就用靜態(tài)工廠方法代替構造器模叙,并且慎重地選擇名稱以便突出它們之間的區(qū)別不必每次調用它們的時候都創(chuàng)建一個對象歇拆。
能夠為重復的調用返回相同的對象,有助于類總能嚴格控制在某個時刻哪些實例應該存在。這種類被稱作實例受控類(instance-controlled)故觅。編寫實例受控類有幾個原因:確保它是一個Singleton(見第三條 單例)或者 是不可實例化的(見第4條)厂庇。還使得不可變的類(見第15條)可以確保不會存在兩個相等的實例,即當且僅當 a==b 的時候才有 a.equals(b) 為 true输吏,可以提升性能【書這里單詞錯了】它們可以返回原返回類型的任何子類型的對象权旷。
這樣我們在選擇返回對象的類時就有了更大的靈活性。這種靈活性的一種應用是贯溅,API 可以返回對象拄氯,同時又不會使對象的類變成公有的。以這種方式隱藏實現類會使API變得非常簡介盗迟。這項技術適用于基于接口的框架(interface-based-framework坤邪,見第18條 ),在這種框架中罚缕,接口為靜態(tài)工廠方法提供了 自然返回類型艇纺。
自然返回類型(natural return types)
可能有人會對自然返回類型產生疑問,以下是加粗文字的英文原版:
where interfaces provide natural return types for static factory methods.
個人的理解是:此框架靜態(tài)工廠方法返回的類型都是接口類型邮弹,也就是接口類型為靜態(tài)工廠方法提供了返回類型黔衡,這點我會結合下面的 java.util.Collections 源碼(JDK 1.8)說明。
在此框架中腌乡,接口為靜態(tài)工廠方法提供了自然返回類型盟劫。接口不能有靜態(tài)方法,按照慣例与纽,接口 Type 的靜態(tài)工廠方法被放在一個名為Types的不可實例化類(見第4條)中侣签。
書中提了一個 Java Collections Framework 的例子,這里我結合 Collections 的源碼急迂,整理一下思路影所。
首先書中說 Java Collections Framework 的便利實現,幾乎都通過靜態(tài)工廠方法在一個不可實例化的類(java.util.Collections)中導出僚碎。所有返回對象的類都是非公有的猴娩。
構造器私有,不可實例勺阐。
返回對象的類為非公有類(靜態(tài)私有)卷中,此框架靜態(tài)工廠方法返回的類型都是接口類型。
比導出獨立公有類的那種實現方式要小得多渊抽,這不僅僅是指API上的減少蟆豫,也是概念上的減少。
用戶 知道懒闷,被返回的對象是由相關的接口精確指定的十减,所以它們不需要閱讀有關的類文檔徙瓶。使用這種靜態(tài)工廠方法時,甚至要求 客戶端 通過制定接口來引用被返回的對象嫉称,而不是通過它的實現類來引用被返回的對象,這是一種良好的習慣(見第52條)【客戶與用戶的差別在第一章有說明】
公有的靜態(tài)工廠方法返回的對象類不僅可以是非公有的灵疮,還可以隨著每次調用時的方法參數不同而變化织阅,只要返回的是已聲明返回類型的子類型,都是允許的震捣,為了提升軟件的可維護性和性能荔棉,返回對象的類也可能隨著發(fā)行版本的不同而不同。
書中提到的 java.util.EnumSet 類蒿赢,沒有構造器(抽象類)润樱,可結合閱讀,截圖(JDK1.8)如下:
客戶端永遠不知道也不關心他們從工廠方法種得到的對象的類;只關系它是所需類的某個子類即可。
服務提供者框架
因為篇幅原因兵多,這里以 JDBC 簡要說明敞嗡,詳細內容可自行 google 。
此框架由四個組件組成挥唠,前三個為重要組件,最后一個為可選組件。
- 服務接口(Service Interface):提供者(Mysql Oracle)實現的;
- 提供者注冊 API(Provider Registration API):系統(tǒng)用于注冊實現赂蕴,使客戶端訪問;
- 服務訪問 API(Service access API):客戶端用來獲取服務實例舶胀;
- 服務提供者接口(Service Provider Interface):提供者負責創(chuàng)建其服務實現的實例概说;
對照 JDBC
- 服務接口: Connection
- 提供者注冊API: DriverManager.registerDriver
- 服務訪問API: DriverManager.getConnection
- 服務提供者接口: Driver
其中,Connection 只是一個接口(規(guī)范)嚣伐,各大數據庫廠商【提供者】根據此接口編寫實現類糖赔,讓 DriverManager.registerDriver 注冊,使得客戶端可以使用 DriverManager.getConnection 獲得實例纤控,如果有提供服務提供者接口 Driver 則由其創(chuàng)建服務實現的實例挂捻,如果沒有就按照類名注冊,并通過反射實例化船万。
適配器模式(adapter):
適配器模式的個人理解:實現舊接口刻撒,組合所需類,在舊接口的方法中調用所需類的方法耿导。
此模式除了上述3個優(yōu)點外声怔,還增加了第四個優(yōu)點:
在構建參數化類型實例的時候,它們使代碼變得更加簡潔
在調用參數化類的構造器時舱呻,即使類型參數很明顯醋火,也必須指明悠汽。這通常要求你鏈接兩次提供類型的參數:
Map< String , List< String > > m =
new HashMap< String , List< String > >();
而有了靜態(tài)工廠方法,編譯器舊可以替你找到類型參數芥驳。這被稱為類型推導(type inference)【在泛型章節(jié)也有提到】柿冲。
public static <K , V> HashMap <K , V> newInstance ( ) {
return new HashMap<K , V> ( ) ;
}
Map<String , List < String > > m = HashMap.newInstance ( ) ;
總有一天,Java 將能夠在構造器調用以及方法調用中執(zhí)行這種類型推導兆旬,但到發(fā)行版 1.6 為止暫時還不能這么做【JDK 1.8 也沒有】假抄。標準的 Map 實現如 HashMap 并沒有工廠方法,但是可以將這些方法放入你自己的工具箱丽猬。
靜態(tài)工廠方法的主要缺點:
類如果不含公有的或者受保護的構造器宿饱,就不能被子類化。
鼓勵程序員使用復合(composition)脚祟,而不是繼承(見第16條)它們與其他的靜態(tài)方法 實際上沒有任何區(qū)別谬以。
在 API 文檔中,它們沒有像構造器那樣被明確的標識出來由桌,你可以通過在 類 或者 接口 注釋中關注靜態(tài)工廠为黎,并遵守標準的命名習慣,也可以彌補這一劣勢沥寥。
靜態(tài)工廠方法的一些慣用名稱:
valueOf:不太嚴格的講碍舍,該方法返回的實例與它的參數具有相同的值。這樣的靜態(tài)工廠方法實際上是類型轉換方法邑雅。
of:valueOf 的一種更為簡介的替代片橡,在 EnumSet(見第32條) 中使用并流行起來。
getInstance:返回的實例是通過方法的參數來描述的淮野,但是不能夠說與參數具有同樣是值捧书。對于 Singleton 【單例】來說,該方法沒有參數骤星,并返回唯一的實例经瓷。
newInstance:像 getInstance 一樣,但 newInstance 能夠確保返回的每個實例都與所有其他不同洞难。
getType:像 getInstance 一樣舆吮,但是在工廠方法處于不同的類中的時候使用。Type 標識工廠方法返回的類型對象【getInteger队贱、getLong】色冀。
newType:像 newInstance 一樣,但是在工廠方法處于不同的類中的時候使用柱嫌。Type 標識工廠方法返回的類型對象【newInteger锋恬、newLong】。
簡而言之编丘,靜態(tài)工廠方法 和 公有構造器 各有用處与学,我們需要理解它們各自的長處彤悔。靜態(tài)工廠通常更加合適,因此切記第一反應就是提供公有的構造器索守,而不先考慮靜態(tài)工廠晕窑。