-序言: 1.Java的正式發(fā)行名稱 工程版本號(hào) JDK 1.1.x / JRE 1.1.x 1.1 Java2 Platform,Standard Edition,v 1.2 1.2 Java2 Platform,Standard Edition,v 1.3 1.2 Java2 Platform,Standard Edition,v 1.4 1.2 Java2 Platform,Standard Edition,v 5.0 1.5 Java Platform,Standard Edition,5 1.6 2.術(shù)語: 1.Java語言支持4種類型: 接口(interface) 類(class) 數(shù)組(array) 基本類型(primitive),前3中類型通常被稱為引用類型,類實(shí)例和數(shù)組是對(duì)象,而基本類型的值則不是對(duì)象。 2.類的成員由其域(field),方法(method),成員類(member class)和成員接口(member interface)組成. 3.方法的簽名(signature)由它的名稱和所有的參數(shù)類型組成,簽名不包括它的返回類型。 4.繼承(inheritance),作為子類化(subclassing)的同義詞 5.不再使用接口繼承這種說法,而是簡(jiǎn)單的說一個(gè)類實(shí)現(xiàn)(implement)了另一個(gè)接口或者說一個(gè)接口擴(kuò)展(extend)了另一個(gè)接口. 6.描述沒有指定訪問級(jí)別的情況下所使用的訪問級(jí)別,使用了包級(jí)私有(package-private),而不是如JLS(Java Language Specifications),6.6.1所使用的技術(shù)性術(shù)語"缺省訪問(default access)"級(jí)別. 7.導(dǎo)出的API(exported API)或者簡(jiǎn)單的說API,是指類,接口,構(gòu)造器,成員,和序列化形式,程序員可以通過他們可以訪問類,接口夠或者包。使用API編寫程序的程序員被稱為該API的用戶(user),在類的實(shí)現(xiàn)中使用了API的類被稱為該API的客戶(client). 8.類,接口,構(gòu)造器,成員以及序列化的形式被統(tǒng)稱為API元素(API element),導(dǎo)出的API由所有可在該API的包之外訪問的API元素組成。任何客戶端都可以使用這些API元素,而API的創(chuàng)建者負(fù)責(zé)支持這些API元素。Javadoc工具類在默認(rèn)情況下也正是為這些元素生成文檔,這絕非偶然。不嚴(yán)格的講,一個(gè)類的導(dǎo)出的API是由每個(gè)公有(public)類或者接口中所有公有的或者受保護(hù)的(protected)成員和構(gòu)造器組成. 第一條:考慮用靜態(tài)工廠方法代替構(gòu)造器 1.對(duì)于類來說,為了讓客戶端獲取其自身的一個(gè)實(shí)例,最常用的辦法就是提供一個(gè)公有的構(gòu)造器。還有一種方法,也應(yīng)該在每個(gè)程序員的工具箱中占有一席之地。類可以提供一個(gè)公有的靜態(tài)工廠方法(static factory method),其時(shí)一個(gè)返回類實(shí)例的靜態(tài)方法。如: public static Boolean valueOf(boolean b) { return b ? Boolean.TRUE : Boolean.FALSE; } 注:靜態(tài)工廠方法與設(shè)計(jì)模式的工廠方法模式不同。本條目所指的靜態(tài)工廠方法并不直接對(duì)應(yīng)設(shè)計(jì)模式的工廠方法。 類可以通過靜態(tài)工廠方法提供他的客戶端,而不是通過提供構(gòu)造器 2.優(yōu)勢(shì): 1.靜態(tài)工廠方法與構(gòu)造器不同的第一大優(yōu)勢(shì)在于:他們有名稱。如果構(gòu)造器的參數(shù)本身沒有確切的描述正被返回的對(duì)象,那么具有適當(dāng)名稱的靜態(tài)工廠方法會(huì)更容易使用,產(chǎn)生的客戶端代碼也更易讀。 如:BigInteger(int,int,Random),返回的可能是素?cái)?shù),如果用BigInteger.probablePrime的靜態(tài)工廠方法來表示,顯示更清楚。1.4的發(fā)行版本中最終增加了這個(gè)方法。 2.一個(gè)類只能有一個(gè)帶有指定簽名的構(gòu)造器,編程人員通常避開這一限制是提供兩個(gè)構(gòu)造器,而它們的參數(shù)列表只是在參數(shù)列表順序上有所不同。實(shí)際上這不好。對(duì)于這樣的API,client永遠(yuǎn)也記不住該調(diào)用哪個(gè)構(gòu)造器,結(jié)果通常會(huì)調(diào)用錯(cuò)誤的構(gòu)造器。并且讀到這些構(gòu)造器的代碼時(shí),如果沒有參考文檔,也不知所云。所以由于靜態(tài)工廠方法有名稱,所以他們不受上述限制。當(dāng)一個(gè)類需要多個(gè)帶有相同簽名的構(gòu)造器(只是參數(shù)列表順序不同)時(shí),就用靜態(tài)工廠方法代替構(gòu)造器并且慎重選擇名稱已突出他們之間的區(qū)別。 3.靜態(tài)工廠方法與構(gòu)造器不同的第二大優(yōu)勢(shì)在于,不必再每次調(diào)用它們的時(shí)候都創(chuàng)建一個(gè)新對(duì)象。 這使得不可變類可以使用預(yù)先構(gòu)建好的實(shí)例,或者將構(gòu)建好的實(shí)例緩存起來,進(jìn)行重復(fù)利用,從而避免創(chuàng)建不必要的重復(fù)對(duì)象。Boolean.valueOf(boolean)說明了這項(xiàng)技術(shù),它從來不創(chuàng)建對(duì)象。 這類似于Flyweight模式。如果程序經(jīng)常請(qǐng)求創(chuàng)建相同的對(duì)象,并且創(chuàng)建對(duì)象的代價(jià)很高,則這項(xiàng)技術(shù)可以極大的提高性能。 靜態(tài)工廠方法能夠?yàn)橹貜?fù)的調(diào)用返回相同對(duì)象,這有助于類總能嚴(yán)格控制在某個(gè)時(shí)刻那些實(shí)例應(yīng)該存在。這種類被稱為實(shí)例受控的類。instance-controlled.編寫實(shí)例受控的類有幾個(gè)原因: 1.使得類可以確保它是一個(gè)單例或者是不可實(shí)例化的 2.使得不可變的類確保不會(huì)存在兩個(gè)相等的實(shí)例,即當(dāng)且僅當(dāng)a == b的時(shí)候才有a.equals(b)為true.如果類保證了這一點(diǎn),其客戶端就可以利用==操作符來代替equals(Object)方法,這樣可以提高性能。枚舉類型保證了這一點(diǎn)。 4.靜態(tài)工廠方法與構(gòu)造器不同的第三大優(yōu)勢(shì)在于,它們可以返回原返回類型的任何子類型對(duì)象: 這樣我們?cè)谶x擇返回對(duì)象的類時(shí)就有了更大的靈活性. 這種靈活性的一種應(yīng)用是,API可以返回對(duì)象,同時(shí)又不會(huì)使對(duì)象的類變成公有的,以這種方式因此實(shí)現(xiàn)類會(huì)使API變的非常簡(jiǎn)潔.該項(xiàng)技術(shù)適用于基于接口的框架,interface-based-framework. 接口為靜態(tài)工廠方法提供了自然返回類型。因接口中不能有靜態(tài)方法,按照慣例,接口Type的靜態(tài)工廠方法被放在一個(gè)名為Types的不可實(shí)例化的類中。 例子1: Java Collections Framework的集合接口有32個(gè)便利實(shí)現(xiàn),分別提供了不可修改的集合,同步集合等。幾乎所有這些實(shí)現(xiàn)都通過靜態(tài)工廠方法在一個(gè)不可實(shí)例化的類java.utils.Collections中導(dǎo)出。所有返回對(duì)象都是非公有的。 如: public static <T> Collection<T> unmodifiableCollection(Collection<? extends T> c) { return new UnmodifiableCollection<>(c); } static class UnmodifiableCollection<E> implements Collection<E>, Serializable UnmodifiableCollection定義在Collections中,是非public。 現(xiàn)在Collections Framework Api被導(dǎo)出32個(gè)獨(dú)立公有類的那種實(shí)現(xiàn)方式要小的多,每種便利實(shí)現(xiàn)都對(duì)應(yīng)一個(gè)類。這不僅僅是指API數(shù)量上的減少,也是概念意義上的減少,用戶知道被返回的對(duì)象是由相關(guān)的接口精確指定的,所以它們不需要閱讀相關(guān)的類文檔。使用這種靜態(tài)工廠方法時(shí),甚至要求客戶端通過接口來引用被返回的對(duì)象,而不是通過它的實(shí)現(xiàn)類來引用被返回的對(duì)象,這是一種良好的習(xí)慣。 公有的靜態(tài)工廠方法所返回的對(duì)象的類不僅可以是非公有的,而且該類還可以隨著每次調(diào)用而發(fā)生變化,這取決于靜態(tài)工廠方法的參數(shù)值。只要是已生命的返回類型的子類型,都是允許的。 為了維護(hù)軟件的可維護(hù)性和性能,返回的對(duì)象的類也可能隨著發(fā)行版本的不同而不同。 如:java.util.EnumSet,沒有公用構(gòu)造器,只有靜態(tài)工廠方法。他們返回兩種實(shí)現(xiàn)類之一,具體取決于底層枚舉數(shù)組的大小;如果它的元素有64個(gè)或者更少,就像大多數(shù)枚舉類型一樣,靜態(tài)工廠方法返回一個(gè)RegularEnumSet實(shí)例,用單個(gè)long支持;如果枚舉類型有65個(gè)或者更多,工廠就返回JumboEnumSet實(shí)例,用long數(shù)組進(jìn)行支持。 如: public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) { Enum[] universe = getUniverse(elementType); if (universe == null) throw new ClassCastException(elementType + " not an enum"); if (universe.length <= 64) return new RegularEnumSet<>(elementType, universe); else return new JumboEnumSet<>(elementType, universe); } 這兩個(gè)實(shí)現(xiàn)類的存在對(duì)于客戶端來說是不可見的。如果RegularEnumSet不能再給小的枚舉類型提供性能優(yōu)勢(shì),就可能從未來的發(fā)行版本中將它刪除,不會(huì)造成不良的影響。同樣的了,如果證明對(duì)性能要好處,也可能在未來的發(fā)行版本中添加第三甚至第四個(gè)Enum實(shí)現(xiàn)。客戶端永遠(yuǎn)也不知道也不關(guān)心他們從工廠方法得到的對(duì)象的類,他們只關(guān)心他是EnumSet的某個(gè)子類即可。 class RegularEnumSet<E extends Enum<E>> extends EnumSet<E>,非public. class JumboEnumSet<E extends Enum<E>> extends EnumSet<E>,非public. 靜態(tài)工廠方法返回的對(duì)象所屬的類,在編寫包含該靜態(tài)工廠方法的類時(shí)可以不必存在。這種靈活的靜態(tài)工廠方法構(gòu)成了服務(wù)提供者框架,Service Provider Framework的基礎(chǔ)。如JDBC API。服務(wù)提供者框架是指這樣一個(gè)系統(tǒng),多個(gè)服務(wù)提供者實(shí)現(xiàn)一個(gè)服務(wù),系統(tǒng)為服務(wù)提供者的客戶端提供多個(gè)實(shí)現(xiàn),把它們從多個(gè)實(shí)現(xiàn)中解耦出來。 服務(wù)器提供者框架中有三個(gè)重要的組件:服務(wù)接口(Service Interface),這是提供者實(shí)現(xiàn)的,提供者注冊(cè)API,provider registration API,這是系統(tǒng)用來注冊(cè)實(shí)現(xiàn),讓客戶端訪問它們的;服務(wù)訪問API,Service Access API,是客戶端用來獲取服務(wù)實(shí)例的。服務(wù)訪問API一般允許但是不要求客戶端指定某種選擇提供者的條件.如果沒有這樣的規(guī)定,API就會(huì)返回默認(rèn)實(shí)現(xiàn)的 一個(gè)實(shí)例。服務(wù)訪問API是靈活的靜態(tài)工廠,它構(gòu)成了服務(wù)提供者框架的基礎(chǔ). 服務(wù)提供者框架的第四個(gè)組件是可選的:服務(wù)提供者接口,Service Provider Interface,這些提供者負(fù)責(zé)創(chuàng)建器服務(wù)實(shí)現(xiàn)的實(shí)例。如果沒有服務(wù)提供者接口,實(shí)現(xiàn)就按照類名來注冊(cè),并通過反射方式進(jìn)行實(shí)例化。對(duì)于JDBC來說,Connection就是它的服務(wù)接口,DriverManager.registerDriver就是提供者注冊(cè)API,DriverManager.getConnection是服務(wù)訪問API,Driver就是服務(wù)提供者接口。 服務(wù)提供者框架模式有著無數(shù)種變體.,如服務(wù)訪問API可以利用適配器模式,返回被提供者需要的更豐富的服務(wù)接口: 5.靜態(tài)工廠方法的第四大優(yōu)勢(shì)在于,在創(chuàng)建參數(shù)化類型實(shí)例的時(shí)候,他們使代碼變得更加簡(jiǎn)潔。 如在使用參數(shù)化的類的構(gòu)造器時(shí),即時(shí)類型參數(shù)很明顯,也必須指明。如:通常要求你接連兩次提供類型參數(shù). Map<String,List<String>> m = new HashMap<String,List<String>> 隨著類型參數(shù)越來越長(zhǎng),越來越復(fù)雜,這一冗長(zhǎng)的說明也很快變的痛苦起來。如果有靜態(tài)工廠方法,編譯器就可以替你找到類型參數(shù)。這被稱為類型推倒,type inference.如假設(shè)HashMap提供了這個(gè)工廠工廠: public static <K,V> HashMap<K,V> newInstance() { return new HashMap<K,V>(); } 你就可以簡(jiǎn)潔的代碼替換上面繁瑣的說明: Map<String,List<String>> m = HashMap.newInstance(); 目前1.6版本的標(biāo)準(zhǔn)集合還沒有提供工廠方法,但是可以把這些方法放在你自己的工具類中。更重要的是,可以把這樣的靜態(tài)工廠放在你自己的參數(shù)化的類中。 6.靜態(tài)工廠方法的主要缺點(diǎn)在于,類如果不含公有或者受保護(hù)的構(gòu)造器,就不能被子類化。 對(duì)于公有的靜態(tài)工廠所返回的非公有類,也同樣如此。如,你想講Collections Framework中的任何方便的實(shí)現(xiàn)類子類化,是不可能的。 但是這樣有時(shí)候也有好處,即它鼓勵(lì)程序員使用復(fù)合compostion,而不是繼承。 7.靜態(tài)工廠方法的第二個(gè)缺點(diǎn)在于,他們與其他的靜態(tài)方法實(shí)際上沒有任何區(qū)別。在API文檔中,他們沒有像構(gòu)造器那樣在API文檔中明確標(biāo)識(shí)出來,因此對(duì)于提供了靜態(tài)工廠方法而不是構(gòu)造器的類來說,要想查明如何實(shí)例化一個(gè)類,這是非常困難的。javadoc工具總有一天會(huì)注意到靜態(tài)工廠方法。同時(shí),你通過在類或者接口注釋中關(guān)注靜態(tài)工廠,并遵守標(biāo)準(zhǔn)的命名習(xí)慣,也可以彌補(bǔ)這一劣勢(shì)。下面是靜態(tài)工廠的一些慣用名稱: 1.valueOf-不太嚴(yán)格講,該方法返回的實(shí)例與它的參數(shù)具有相同的值。這樣的靜態(tài)工廠方法實(shí)際上是類型轉(zhuǎn)換方法。 2.of-valueOf的一種更為簡(jiǎn)潔的替代,在EnumSet中使用并流行起來。 3.getInstance,返回的實(shí)例是通過方法的參數(shù)來描述的。但是不能夠說與參數(shù)具有同樣的值。對(duì)于單例Singleton來說,該方法沒有參數(shù),并返回唯一的實(shí)例。 4.newInstance,向getInstance一樣,但newInstance能夠確保返回的每個(gè)實(shí)例都與所有其他實(shí)例不同。 5.getType,就像getInstance一樣,但是在工廠方法處于不同的類的時(shí)候使用(子類)。Type表示工廠方法所返回的對(duì)象類型。 6.newType,就像newInstance一樣,但是在工廠方法處于不同的類的時(shí)候使用(子類)。Type表示工廠方法所返回的對(duì)象類型。 總之,靜態(tài)工廠方法與公共構(gòu)造器都各有好處,我們需要理解他們各自的長(zhǎng)處。靜態(tài)工廠通常更加合適,因此切記第一反應(yīng)就是提供公有的構(gòu)造器,而不先考慮靜態(tài)工廠。
部分源碼:
package com.book.chap2.staticFactory;


/** *//**
*
*服務(wù)接口
*Service interface
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-24
*
*/

public interface Service


{

}

package com.book.chap2.staticFactory;


/** *//**
*
*服務(wù)提供者接口
*Service Provider interface
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-24
*
*/

public interface Provider


{
Service newService();
}

package com.book.chap2.staticFactory;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/** *//**
*
*非實(shí)例化的類,用來注冊(cè)和訪問服務(wù)
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-24
*
*/

public class Services


{
private Services()

{
}
//服務(wù)提供者map
private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
//默認(rèn)的服務(wù)提供者名字
public static final String DEFAULT_PROVIDER_NAME = "<def>";

/** *//**
*
* 注冊(cè)默認(rèn)的Provider
*
* @param p
*/
public static void registerDefaultProvider(Provider p)

{
registerProvider(DEFAULT_PROVIDER_NAME, p);
}

/** *//**
*
* 注冊(cè)Provider
*
* @param name
* @param p
*/
public static void registerProvider(String name,Provider p)

{
providers.put(name, p);
}
//服務(wù)訪問api Service access API
public static Service newInstance()

{
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name)

{
Provider p = providers.get(name);
if(p == null)

{
throw new IllegalArgumentException("No Provider registered with name:" + name);
}
return p.newService();
}
}
package com.book.chap2.staticFactory;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


/** *//**
*
*參數(shù)化實(shí)例,使用靜態(tài)工廠,會(huì)使代碼簡(jiǎn)單很多
*<p>使用類型推斷</p>
*
*@author landon
*@since 1.6.0_35
*@version 1.0.0 2012-12-26
*
*/

public class TypeInference


{

/** *//**
*
* 使用類型推斷的初始化HashMap方式
*
* @return
*/
public static <K,V> HashMap<K, V> newInstance()

{
return new HashMap<K, V>();
}
public static void main(String
args)

{
//普通方式初始化Map
Map<String, List<String>> commonMap = new HashMap<String, List<String>>();
//類型推斷的初始化方式
Map<String, List<String>> inferMap = TypeInference.newInstance();
}
}

posted on 2013-03-15 14:57
landon 閱讀(2342)
評(píng)論(2) 編輯 收藏 所屬分類:
Program 、
Book