這里更準(zhǔn)確的說(shuō), 是替代 public 的構(gòu)造器. 這里的靜態(tài)工廠方法指的是類(lèi)中的一個(gè)靜態(tài)方法, 返回該類(lèi)的一個(gè)實(shí)例 (instance). 例如 Java 的 Boolean 包裝類(lèi)就提供了如下的靜態(tài)工廠方法:
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
書(shū)中為我們概括了, 使用靜態(tài)工廠方法有如下優(yōu)點(diǎn):
- 靜態(tài)工廠方法與構(gòu)造器 (constructors) 相比, 是可以具有名稱(chēng)的.
- 與構(gòu)造起相比, 靜態(tài)工廠方法并不要求每次調(diào)用的時(shí)候都重新創(chuàng)建一個(gè)新的對(duì)象, 這樣可以避免創(chuàng)建很多不必要的對(duì)象.
- 第三點(diǎn)優(yōu)勢(shì)是, 靜態(tài)工廠方法不僅可以創(chuàng)建當(dāng)前類(lèi)的對(duì)象, 而且可以返回返回類(lèi)型的任何子類(lèi)對(duì)象.
- 第四點(diǎn)優(yōu)勢(shì)是, 靜態(tài)工廠方法返回的類(lèi)型可以根據(jù)輸入?yún)?shù)的不同而不同.
- 第五點(diǎn)優(yōu)勢(shì)在于, 靜態(tài)工廠方法返回的對(duì)象, 在編寫(xiě)靜態(tài)方法時(shí), 其對(duì)應(yīng)的類(lèi)可以不存在.
靜態(tài)工廠方法與構(gòu)造器 (constructors) 相比, 是可以具有名稱(chēng)的
我們?cè)趧?chuàng)建類(lèi)的時(shí)候, 有時(shí)候需要不止一種方式來(lái)產(chǎn)生對(duì)象, 一種方式是對(duì)構(gòu)造器的重載, 但是對(duì)構(gòu)造器方法的重載, 只能通過(guò)不同的參數(shù)來(lái)實(shí)現(xiàn). 有時(shí)候, 我們使用同樣的參數(shù), 也想要使用不同的方式來(lái)構(gòu)造對(duì)象, 這對(duì)于使用構(gòu)造器來(lái)說(shuō)是很難實(shí)現(xiàn)的.
使用靜態(tài)工廠方法, 我們可以使用不同的命名的方式, 來(lái)使用不同的方式來(lái)構(gòu)建對(duì)象.
例如, Boolean 類(lèi), 創(chuàng)建 Boolean 對(duì)象的方法有如下幾種:
Boolean(boolean)
Boolean(String)
valueOf(boolean)
valueOf(String)
前兩個(gè)是 Boolean 的公有構(gòu)造器 (public constructor), 都接收一個(gè)參數(shù), 第一個(gè)接收 boolean 類(lèi)型, 第二個(gè)接收 String 類(lèi)型. 都可以分別將對(duì)應(yīng)的值轉(zhuǎn)換為 Boolean 對(duì)象.
但是這里使用構(gòu)造器意義不明確, 這兩個(gè)方法其實(shí)對(duì)應(yīng)著下面的兩個(gè) valueOf()
方法. 這兩種用法其實(shí)是一致的, 但是, valueOf
的語(yǔ)意更加明確一些, valueOf 從語(yǔ)意上來(lái)說(shuō), 就是一種類(lèi)型轉(zhuǎn)換, 代表著將 boolean 類(lèi)型或者 String 類(lèi)型轉(zhuǎn)換成 Boolean 的對(duì)象.
下面是一個(gè)項(xiàng)目中實(shí)際使用, 更加具有實(shí)際意義的例子. 在構(gòu)建網(wǎng)絡(luò)接口, 確定網(wǎng)絡(luò)接口的返回值的時(shí)候, 我們通常需要進(jìn)行一定的封裝. 例如, 如果將所有的返回類(lèi)型都封裝在一個(gè)叫做 ResponseModel<M>
的泛型類(lèi)中, 加入包含以下基本信息.
public class ResponseModel<M> {
// 返回代碼
private int code;
// 描述信息
private String message;
// 創(chuàng)建時(shí)間
private LocalDateTime time = LocalDateTime.now();
// 具體的內(nèi)容
private M result;
}
如果我們寫(xiě)一個(gè)服務(wù)程序, 那么這個(gè)類(lèi)將是我們與客戶端進(jìn)行溝通的一個(gè)非常常用的類(lèi), 我們經(jīng)常需要?jiǎng)?chuàng)建不同的 ResponseModel 來(lái)返回給客戶端. 因此我們最好提供不同的方法來(lái)能夠快速的使用不同的方法來(lái)創(chuàng)建不同的 ResponseModel. 例如, 請(qǐng)求成功的 Response (200), 包含不同類(lèi)型的錯(cuò)誤信息的 Response 等等. 如果使用構(gòu)造器來(lái)實(shí)現(xiàn), 是很難實(shí)現(xiàn)的, 我們需要非常謹(jǐn)慎的構(gòu)建不同的重載來(lái)實(shí)現(xiàn), 并且在調(diào)用時(shí)也是非沉酰混亂的, 因?yàn)樵谶@種情況下每一個(gè)構(gòu)造方法的參數(shù)的不同并不能提供非常明確的語(yǔ)意以表示我們要?jiǎng)?chuàng)建的對(duì)象, 這可能會(huì)導(dǎo)致很大程度上的混亂, 使用上也非常不便.
如果使用靜態(tài)工廠方法, 我們就可以通過(guò)給不同的方法進(jìn)行命名, 來(lái)提供非常明確的語(yǔ)意信息來(lái)快速的構(gòu)建所需的對(duì)象. 例如:
public static <M> ResponseModel<M> buildOk() {
return new ResponseModel<M>();
}
public static <M> ResponseModel<M> buildOk(M result) {
return new ResponseModel<M>(result);
}
public static <M> ResponseModel<M> buildParameterError() {
return new ResponseModel<M>(ERROR_PARAMETERS, "Parameters Error.");
}
上面就分別列舉了幾種不同的靜態(tài)工廠方法, 通過(guò)方法的名稱(chēng)就可以非常明確的知道我們所構(gòu)建的對(duì)象的含義, 真正意義上的提供了快捷方法.
靜態(tài)工廠方法并不要求每次調(diào)用的時(shí)候都重新創(chuàng)建一個(gè)新的對(duì)象, 這樣可以避免創(chuàng)建很多不必要的對(duì)象.
這種機(jī)制對(duì)于很多值類(lèi)來(lái)說(shuō), 是很常用的, 一個(gè)非常典型的例子就是 Java 中的那些裝箱類(lèi).
在 Java 中共有 8 種 primitive 類(lèi)型, 這八種基本數(shù)據(jù)類(lèi)型對(duì)應(yīng) 8 種自動(dòng)裝箱類(lèi):
- char -> Character
- boolean -> Boolean
- byte -> Byte
- short -> Short
- int -> Integer
- long -> Long
- float -> Float
- double -> Double
Boolean 類(lèi)
Boolean 類(lèi)是一個(gè)比較簡(jiǎn)單的類(lèi), Boolean 的可行值實(shí)際上只有兩個(gè), True 和 False. 因此, 理論上, 在運(yùn)行過(guò)程中, Boolean 類(lèi)最多只需要?jiǎng)?chuàng)建兩個(gè)對(duì)象即可. 在 Boolean 類(lèi)內(nèi)部也是這樣實(shí)現(xiàn)的, Boolean 內(nèi)部包含兩個(gè)靜態(tài)成員:
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code true}.
*/
public static final Boolean TRUE = new Boolean(true);
/**
* The {@code Boolean} object corresponding to the primitive
* value {@code false}.
*/
public static final Boolean FALSE = new Boolean(false);
在我們使用 Boolean 對(duì)象時(shí), 應(yīng)該始終使用的是這兩個(gè)對(duì)象, 避免創(chuàng)建額外的變量, 這樣能夠方便我們使用.
Boolean 的 public 構(gòu)造方法在新版本的 JDK 中已經(jīng)被標(biāo)記為 @Deprecated
.
@Deprecated(since="9")
public Boolean(boolean value) {
this.value = value;
}
@Deprecated(since="9")
public Boolean(String s) {
this(parseBoolean(s));
}
在使用 Boolean 時(shí), 我們應(yīng)該使用其靜態(tài)工廠方法 valueOf()
來(lái)創(chuàng)建 Boolean 對(duì)象, 或者直接使用靜態(tài)成員 TRUE
和 FALSE
.
關(guān)于自動(dòng)裝箱和自動(dòng)拆箱, 我查到有資料說(shuō)是會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的 valueOf()
方法.
只要不在外部調(diào)用 Boolean 的構(gòu)造方法 (我們也不應(yīng)該調(diào)用), 程序在運(yùn)行過(guò)程中就只存在兩個(gè)靜態(tài)對(duì)象.
Integer 類(lèi)
Integer 相對(duì)較復(fù)雜一些, 但是該類(lèi)在設(shè)計(jì)時(shí), 同樣擁有靜態(tài)工廠方法, 來(lái)代替構(gòu)造器. Integer 的構(gòu)造器同樣也被標(biāo)記為 @Deprecated
, 我們同樣不應(yīng)該使用.
@Deprecated(since="9")
public Integer(int value) {
this.value = value;
}
@Deprecated(since="9")
public Integer(String s) throws NumberFormatException {
this.value = parseInt(s, 10);
}
同樣的, 用于替代上面兩個(gè)構(gòu)造方法的靜態(tài)工廠方法是 valueOf(String s, int radix)
, valueOf(String s)
以及 valueOf(int i)
. 前兩個(gè)構(gòu)造函數(shù)是從 String 轉(zhuǎn)換成 Integer 對(duì)象, radix 表示進(jìn)制.
Integer 與 Boolean 相比的額外的機(jī)制是緩存機(jī)制, Boolean 對(duì)象只有兩個(gè)取值, 因此直接使用兩個(gè)靜態(tài)成員變量即可.
Integer 使用了額外的緩存機(jī)制, Integer 中有一個(gè)靜態(tài)成員類(lèi) IntegerCache
, 這是一個(gè)單例類(lèi), 使用靜態(tài)代碼塊進(jìn)行了初始化.
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
VM.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
- IntegerCache 的緩存范圍默認(rèn)是 -128 ~ 127.
- 在實(shí)現(xiàn)過(guò)程中, 緩存的下界只允許默認(rèn)值, 而上界允許通過(guò)設(shè)置虛擬機(jī)參數(shù)的方式進(jìn)行修改.
String integerCacheHighPropValue = VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
- 可以通過(guò)虛擬機(jī)參數(shù)
-XX:AutoBoxCacheMax=<size>
來(lái)設(shè)置緩存的上界. 在 JVM 初始化時(shí), 會(huì)將該值緩存到jdk.internal.misc.VM
中的java.lang.Integer.IntegerCache.high
屬性中. - 關(guān)于為什么只允許修改上界而不允許修改下界, 我查到 Why does the JVM allow to set the “high” value for the IntegerCache, but not the “l(fā)ow”? 這個(gè)問(wèn)題. 他表示這是個(gè)問(wèn)題, 但是沒(méi)有需求去修改下界.
- RFP 官方的說(shuō)法.
- 這個(gè)參數(shù)的調(diào)整只在 Integer 中存在, 在 Long 中并沒(méi)有任何可調(diào)整的緩存參數(shù), 都是設(shè)置的固定值.
- 在實(shí)現(xiàn)過(guò)程中, 還有一點(diǎn)需要注意的是, 緩存使用了 CDS 機(jī)制 (Class Data Sharing).
由于有上述緩存機(jī)制, 我們進(jìn)行如下測(cè)試:
@SuppressWarnings({"NumberEquality"})
public static void testInteger() {
System.out.println("\n=====Some test for Integer=====\n");
Integer a = 1000, b = 1000;
System.out.println("a = " + a + ", b = " + b);
// Warning: Only for test, don't use "==" to compare two boxed object
System.out.println("a == b: " + (a == b));
Integer c = 100, d = 100;
System.out.println("c = " + c + ", d = " + d);
// Warning: Only for test, don't use "==" to compare two boxed object
System.out.println("a == b: " + (c == d));
}
對(duì)于上述代碼的如下輸出結(jié)果就比較容易理解了:
=====Some test for Integer=====
a = 1000, b = 1000
a == b: false
c = 100, d = 100
a == b: true
Char, Byte, Long 和 Short
Char, Byte, Long 和 Short 的實(shí)現(xiàn)機(jī)制和 Integer 幾乎一致, 提供了一致的靜態(tài)工廠方法, 同時(shí)也使用了緩存機(jī)制, 這里就不再贅述了.
Double, Float
Double 和 Float 也提供了靜態(tài)工廠方法 valueOf()
, 但是并沒(méi)有提供緩存機(jī)制, 因?yàn)樾?shù)并不適合進(jìn)行緩存.
靜態(tài)工廠方法不僅可以創(chuàng)建當(dāng)前類(lèi)的對(duì)象, 而且可以返回返回類(lèi)型的任何子類(lèi)對(duì)象.
這一特性的一個(gè)應(yīng)用是在不暴露子類(lèi)的具體實(shí)現(xiàn)的情況下, 返回一個(gè)子類(lèi)對(duì)象. 例如 Java Collections Framework. 在一個(gè)叫做 java.util.Collections 的伴生類(lèi)中, 實(shí)現(xiàn)了不可修改集合 (UnmodifiableSet), 同步集合 (SynchronizedSet), 空集合 (EmptySet) 等等這些工具集合.
這些集合的實(shí)現(xiàn), 都是非公有的, 如果想要獲取這些類(lèi)的對(duì)象, 就可以調(diào)用 Collections 中對(duì)應(yīng)的靜態(tài)工廠方法, 并使用接口去引用這些對(duì)象.
靜態(tài)工廠方法返回的類(lèi)型可以根據(jù)輸入?yún)?shù)的不同而不同.
EnumSet 是一個(gè)抽象類(lèi), 其沒(méi)有提供公有構(gòu)造方法, 其提供了一系列的靜態(tài)工廠方法來(lái)創(chuàng)建 EnumSet, 包括 noneOf()
, allOf()
, of()
, range()
. 這一系列靜態(tài)工廠方法最終調(diào)用的都是 noneOf()
方法.
noneOf()
方法傳入的參數(shù)是一個(gè)枚舉類(lèi)的類(lèi)型信息, 其源碼實(shí)現(xiàn)如下.
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
// 獲取所有枚舉的數(shù)組
Enum<?>[] universe = getUniverse(elementType);
if (universe == null)
throw new ClassCastException(elementType + " not an enum");
// 根據(jù)枚舉元素的個(gè)數(shù), 確定具體的 EnumSet 實(shí)現(xiàn)方式
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
可以看出, 其最終返回的是一個(gè) RegularEnumSet
對(duì)象或者 JumboEnumSet
對(duì)象. 這兩個(gè)類(lèi)都是 EnumSet 的具體實(shí)現(xiàn). 是根據(jù)枚舉元素的具體個(gè)數(shù), 從而確定 EnumSet 的具體實(shí)現(xiàn).
RegularEnumSet 內(nèi)部使用單個(gè) long 類(lèi)型進(jìn)行支持:
/**
* Bit vector representation of this set. The 2^k bit indicates the
* presence of universe[k] in this set.
*/
private long elements = 0L;
當(dāng)元素個(gè)數(shù)小于等于 64 個(gè)時(shí), 使用 RegularEnumSet 就足夠了, 因?yàn)橐粋€(gè) long 類(lèi)型的數(shù)據(jù)時(shí) 64 位.
當(dāng)元素個(gè)數(shù)大于 64 時(shí), 使用 JumboEnumSet 實(shí)現(xiàn), 其內(nèi)部使用一個(gè) long 數(shù)組進(jìn)行存儲(chǔ).
/**
* Bit vector representation of this set. The ith bit of the jth
* element of this array represents the presence of universe[64*j +i]
* in this set.
*/
private long elements[];
靜態(tài)工廠方法返回的對(duì)象, 在編寫(xiě)靜態(tài)方法時(shí), 其對(duì)應(yīng)的類(lèi)可以不存在.
其典型應(yīng)用時(shí) JDBC 的應(yīng)用模式.