通常筒占,我們使用new來創(chuàng)建一個(gè)對象舌涨。這應(yīng)該是最簡單的一種方式。但一個(gè)優(yōu)秀的程序員需要考慮具體的應(yīng)用場景以及性能等問題抓半,從而編寫出優(yōu)秀的代碼喂急。那么我們就從創(chuàng)建對象開始,了解如何創(chuàng)建對象笛求?
本文通過靜態(tài)工廠方法與公有構(gòu)造器的比較廊移,分析各自的優(yōu)缺點(diǎn),提供不同場景下創(chuàng)建對象的推薦方式涣易。
創(chuàng)建對象的兩種方式:
方法1: 使用類公有構(gòu)造器画机。
方法2:使用類的靜態(tài)工廠方法返回一個(gè)實(shí)例。
什么是靜態(tài)工廠方法新症?
靜態(tài)工廠方法(static factory methods)即是static修飾的返回類的實(shí)例的一種靜態(tài)方法步氏。
下面是一個(gè)靜態(tài)工廠方法的簡單示例(摘自JDK 1.7 java.lang.Boolean)。
public final class Boolean implements java.io.Serializable,
Comparable<Boolean> {
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}
如果需要獲取一個(gè)Boolean對象徒爹,常規(guī)的方法是new Boolean(true)荚醒,但是也可以如上圖所示Boolean.valueOf(true),這便是靜態(tài)工廠方法隆嗅。
靜態(tài)工廠方法與公有構(gòu)造器的區(qū)別:
1:構(gòu)造方法的名字必須與類名相同界阁。這一特性的優(yōu)點(diǎn)是符合Java語言的規(guī)范,缺點(diǎn)是類的所有重載的構(gòu)造方法的名字都相同胖喳,不能從名字上區(qū)分每個(gè) 重載方法泡躯,容易引起混淆。靜態(tài)工廠方法的方法名可以是任意的丽焊,這一特性的優(yōu)點(diǎn)是可以提高程序代碼的可讀性较剃,在方法名中能體現(xiàn)與實(shí)例有關(guān)的信息。不同的工廠方法有不同名字技健,我們就可以很容易的記住什么方法名可以構(gòu)造什么樣的對象写穴。關(guān)于這一點(diǎn),在 JDK 中最有說服力的一個(gè)例子就是 java.util.concurrent.Executors雌贱,里面N多的靜態(tài)工廠方法:newFixedThreadPool啊送、newSingleThreadExecutor偿短、newCachedThreadPool、newScheduledThreadPool等等
2:每次執(zhí)行new語句時(shí)馋没,都會(huì)創(chuàng)建一個(gè)新的對象昔逗。而靜態(tài)工廠方法每次被調(diào)用的時(shí)候,是否會(huì)創(chuàng)建一個(gè)新的對象完全取決于方法的實(shí)現(xiàn)篷朵。我們調(diào)用靜態(tài)工廠方法返回的可能是緩存的一個(gè)對象纤子,而不是一個(gè)新的對象。這可以減少創(chuàng)建新的對象款票,從來提高性能控硼,前面提到的 Boolean 就是一個(gè)例子。這對于 不可變類的對象特別有用艾少,因?yàn)閷ο蟮牟豢勺冃钥ㄇ恍枰獙?shí)現(xiàn)一個(gè)對象實(shí)例即可。包裝類 Integer 便利用了這種思想缚够,Integer中的靜態(tài)工廠函數(shù)如下:
public static Integer valueOf(int i) {
assert IntegerCache.high >= 127;
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
上面的代碼把出現(xiàn)概率高的 int 做了一個(gè) cache幔妨,這樣每次只要返回 cache 里的對象,而不用新建一個(gè)對象谍椅。
單例模式便是一種利用了靜態(tài)工廠方法來獲得對象實(shí)例的一種方式误堡。確保該類只有一個(gè)對象實(shí)例。
3:new語句只能創(chuàng)建當(dāng)前類的實(shí)例雏吭,而靜態(tài)工廠方法可以返回當(dāng)前類的子類的實(shí)例锁施。這個(gè)特性讓靜態(tài)工廠方法的可擴(kuò)展性大大的優(yōu)于構(gòu)造函數(shù)。在 JDK 中最典型的應(yīng)該是 java.util.EnumSet杖们。EnumSet 本身是 absract悉抵,我們無法直接調(diào)用它的構(gòu)造函數(shù)。不過我們可以調(diào)用它的靜態(tài)方法 noneOf 來創(chuàng)建對象摘完,RegularEnumSet/JumboEnumSet 都繼承至 EnumSet姥饰,noneOf 根據(jù)參數(shù)返回合適的對象。
public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable {
EnumSet(Class<E>elementType, Enum[] universe) {
}
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
}
靜態(tài)工廠方法的缺點(diǎn):
1. 如果一個(gè)類只能通過靜態(tài)工廠方法來獲得實(shí)例孝治,那么該類的構(gòu)造函數(shù)就不能是共有的或受保護(hù)的列粪,那么該類就不會(huì)有子類,即該類不能被繼承谈飒。單例模式中首先要私有化構(gòu)造器岂座。
2.靜態(tài)工廠方法和其他靜態(tài)方法從名字上看無法區(qū)分,建議在命名靜態(tài)工廠方法的時(shí)候遵循一定的規(guī)則步绸。
valueOf — 返回和參數(shù)一樣的對象掺逼,通常都用作類型轉(zhuǎn)換吃媒,比如 Intger.valueOf(int i)
of — 和 valueOf 類似瓤介。
getInstance — 根據(jù)參數(shù)返回對應(yīng)的對象吕喘,該對象可能是緩存在對象池中的對象。對于單例 singleton刑桑,我們使用無參數(shù)的 getInstance氯质,并且總是返回同一個(gè)對象
newInstance — 和 getInstance 一樣,不過這個(gè)方法的調(diào)用每次返回的都是新的對象祠斧。
getType — 和 getInstance 類似闻察,不過區(qū)別是這個(gè)方法返回的對象是另外一個(gè)不同的類。
newType — 和 getType 類似琢锋,不過每次返回的都是一個(gè)新的對象辕漂。
靜態(tài)工廠方法最主要的特點(diǎn)是:每次被調(diào)用的時(shí)候,不一定要?jiǎng)?chuàng)建一個(gè)新的對象吴超。利用這一特點(diǎn)钉嘹,靜態(tài)工廠方法可用來創(chuàng)建以下類的實(shí)例。
<1>單例類:只有惟一的實(shí)例鲸阻。實(shí)例一旦創(chuàng)建跋涣,其屬性值就不會(huì)被改變。
<2>枚舉類:實(shí)例的數(shù)量有限的類鸟悴。
<3>具有實(shí)例緩存的類:能把已經(jīng)創(chuàng)建的實(shí)例暫且存放在緩存中的類陈辱。