記得研一剛進實驗室的時候,看見師兄手裡拿着一本書《Effective Java》,心理想着,這微信大佬(師兄已經被微信錄取)看的書,肯定很有用,是以自己也買來一本喵喵。結果可想而知,看不懂。。。。現在自己再次拿起來看,發現有點意思了。是以把它分享出來。
書擷取方式---->>>1.序
對于一個類,要擷取它的一個執行個體,通常的做法是提供一個公用的構造函數,然而還有另一種方法,我們稱之為靜态工廠方法,實質上也就是一個簡單的靜态方法,它傳回一個類的執行個體。其實,靜态工廠方法擷取對象執行個體,我們并不陌生,來看兩個例子。
構造方法建立對象
在Java中,建立對象常用的方法是通過公有構造方法建立;
舉個例子:如下,是Boolean類的一個構造方法,以及通過該構造方法建立一個Boolean對象;
public Boolean(String s) {
this(toBoolean(s));
}
Boolean bTrue = new Boolean("true");
靜态工廠方法建立對象
其實,建立對象還有另外一種方法,通過公有靜态工廠方法來建立對象,不過這種方法往往容易被程式員忽略;
舉個例子,如下是Boolean類的valueOf方法,以及通過該靜态工廠方法傳回的Boolean執行個體,注意,這裡并沒有建立Boolean執行個體對象,而是傳回事先建立好的Boolean對象;
public static Boolean valueOf(String s) {
return toBoolean(s) ? TRUE : FALSE;
}
Boolean bTrue = Boolean.valueOf("true");
2.優勢
2.1.靜态工廠方法與構造器不同的第一大優勢在于,它們有名稱 (增強了代碼的可讀性)
//使用構造器方法擷取到一個素數
BigInteger prime = new BigInteger(int, int ,Random);
//使用靜态工廠方法
BigInteger prime = BigInteger.probablePrime(int, Random);
詳細示例
假設我們需要寫一個産生随即數的類RandomIntGenerator,該類有兩個成員屬性:最小值min和最大值max,
假設我們的需求是需要建立三種類型的RandomIntGenerator對象,
1、大于min,小于max;
2、大于min 小于Integer.MAX_VALUE;
3、大于Integer.MIN_VALUE 小于max
如果我們不使用靜态工廠方法,代碼一般如下設計:
class RandomIntGenerator
{
/**
* 最小值
*/
private int min = Integer.MIN_VALUE;
/**
* 最大值
*/
private int max = Integer.MAX_VALUE;
/**
* 大于min 小于max
* @param min
* @param max
*/
public RandomIntGenerator(int min, int max)
{
this.min = min;
this.max = max;
}
/**
* 大于min 小于Integer.MAX_VALUE
*/
public RandomIntGenerator(int min)
{
this.min = min;
}
// 報錯:Duplicate method RandomIntGenerator(int) in type RandomIntGenerator
// /**
// * 大于Integer.MIN_VALUE 小于max
// */
// public RandomIntGenerator(int max)
// {
// this.max = max;
// }
}
觀察以上代碼,我們發現,以上代碼不僅可讀性差(new RandomIntGenerator(1, 10)與new RandomIntGenerator(10),不查文檔,不看注釋很難知道其建立的對象的具體含義),而且在設計最後一個構造方法的時候,還報錯,因為已經存在一個參數一緻的工作方法了,提示重複定義;
那麼假設我們使用靜态工廠方法會怎樣呢,如下所示:
class RandomIntGenerator
{
/**
* 最小值
*/
private int min = Integer.MIN_VALUE;
/**
* 最大值
*/
private int max = Integer.MAX_VALUE;
/**
* 大于min 小于max
* @param min
* @param max
*/
public RandomIntGenerator(int min, int max)
{
this.min = min;
this.max = max;
}
/**
* 大于min 小于max
* @param min
* @param max
*/
public static RandomIntGenerator between(int min, int max)
{
return new RandomIntGenerator(min, max);
}
/**
* 大于min 小于Integer.MAX_VALUE
*/
public static RandomIntGenerator biggerThan(int min)
{
return new RandomIntGenerator(min, Integer.MAX_VALUE);
}
/**
* 大于Integer.MIN_VALUE 小于max
*/
public static RandomIntGenerator smallerThan(int max)
{
return new RandomIntGenerator(Integer.MIN_VALUE, max);
}
}
成功滿足需求:建立三種類型的RandomIntGenerator對象,而且建立對象的時候,代碼可讀性比使用構造方法強;
2.2.不必在每次調用它們的時候都建立一個新對象(提升性能)
通過構造方法建立對象的時候,每次都要new一個新對象。而通過靜态方法,可以選擇每次采用單例模式來複用對象,這對于一個性能要求高的系統,可以顯著提高運作速度。
詳細解析:2.3.靜态工廠方法可以傳回原傳回類型的任何子類型對象(增大程式靈活性,減少API數量)
這樣使我們在選擇傳回對象的類時就有了更大的靈活性。
class Father {
private Father() {
}
public static Father newInstance(String type) {
if (type.equals("ChildA")) { // 根據類型判斷傳回那個子類對象
return new ChildA();
} else {
return new ChildB();
}
}
public void getName() {
System.out.println("My name is father");
}
private static class ChildA extends Father {
public void getName() {
System.out.println("My name is child A");
}
}
private static class ChildB extends Father {
public void getName() {
System.out.println("My name is child B");
}
}
}
public class Test {
public static void main(String[] args) {
Father c1 = Father.newInstance("ChildA");
c1.getName();
Father c2 = Father.newInstance("ChildB");
c2.getName();
}
}
2.4.可以使代碼變得更加簡潔(具體類型取決于傳入的參數)
2.4.1 示例1
package tmp;
class MyMap<K,V> {
/**
*
*/
public MyMap()
{
}
public static <K,V> MyMap<K,V> newInstance(){
return new MyMap<K, V>();
}
}
public class Main
{
public static void main(String[] args)
{
MyMap<String, String> map1 = new MyMap<String, String>();
//更加簡潔,不需要重複指明類型參數,可以自行推導出來
MyMap<String, String> map2 = MyMap.newInstance();
}
}
但實際上,現在Java最新的版本已經構造函數已經不需要補充參數了。
HashMap<String,List<String>> map = new HashMap<>();
2.4.2 示例2
//傳統寫法Map<String, String> map =new HashMa<String,String>();
//用guava寫法Map<String, String> map = Maps.newHashMap();
3.缺點
3.1.類如果不含有公有的類或者受保護的構造器,就不能被子類化
如下類,不能被其它類繼承;
class MyMap<K,V> {
/**
*
*/
private MyMap()
{
}
public static <K,V> MyMap<K,V> newInstance(){
return new MyMap<K, V>();
}
}
3.2.它們與其他的靜态方法實際上沒什麼差別,是以我們約定了一些靜态工廠方法的常用名稱
- 1.valueOf —— from 和 to 更為詳細的替代 方式,例如:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- 2.of —— 聚合方法,接受多個參數并傳回該類型的執行個體,并把他們合并在一起,例如:
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- 3.instance 或 getinstance —— 傳回一個由其參數 (如果有的話) 描述的執行個體,但不能說它具有相同的值,例如:
StackWalker luke = StackWalker.getInstance(options);
- 4.create 或 newInstance —— 與 instance 或 getInstance 類似,除此之外該方法保證每次調用傳回一個新的執行個體,例如:
Object newArray = Array.newInstance(classObject, arrayLen);
- 5.getType —— 與 getInstance 類似,但是在工廠方法處于不同的類中的時候使用。getType 中的 Type 是工廠方法傳回的對象類型,例如:
FileStore fs = Files.getFileStore(path);
- 6.newType —— 與 newInstance 類似,但是在工廠方法處于不同的類中的時候使用。newType中的 Type 是工廠方法傳回的對象類型,例如:
BufferedReader br = Files.newBufferedReader(path);
4.補充知識點
4.1.guava将很多集合類做了靜态工廠方法封裝
//用guava的工具類
List<String> list = Lists.newArrayList();
Set<String> set = Sets.newHashSet();
Map<String, String> map = Maps.newHashMap();
//而不是下面這種方式
Map<String, String> map =new HashMa<String,String>();
4.2.什麼是immutable(不可變)對象
- 在多線程操作下,是線程安全的
- 所有不可變集合會比可變集合更有效的利用資源
- 中途不可改變
ImmutableList<String> immutableList = ImmutableList.of("1","2","3","4");
這聲明了一個不可變的List集合,List中有資料1,2,3,4。類中的 操作集合的方法(譬如add, set, sort, replace等)都被聲明過期,并且抛出異常。 而沒用guava之前是需要聲明并且加各種包裹集合才能實作這個功能
// add 方法 @Deprecated @Override
public final void add(int index, E element) {
throw new UnsupportedOperationException();
}
當我們需要一個map中包含key為String類型,value為List類型的時候
//以前我們是這樣寫的
Map<String,List<Integer>> map = new HashMap<String,List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
map.put("aa", list);
System.out.println(map.get("aa"));//[1, 2]
//現在這麼寫
Multimap<String,Integer> map = ArrayListMultimap.create();
map.put("aa", 1);
map.put("aa", 2);
System.out.println(map.get("aa")); //[1, 2]
4.3.注意區分靜态工廠方法和工廠方法模式
注意,這裡的靜态工廠方法與設計模式裡的工廠方法模式不是一個概念:
靜态工廠方法 通常指的是某個類裡的靜态方法,通過調用該靜态方法可以得到屬于該類的一個執行個體;
工廠方法模式 是一種設計模式,指的是讓具體的工廠對象負責生産具體的産品對象,這裡涉及多種工廠(類),多種對象(類),如記憶體工廠生産記憶體對象,CPU工廠生産CPU對象;
不過,如果要說相似的話,靜态工廠方法跟簡單工廠模式倒有那麼點像,不過差別也挺大,簡單工廠模式裡的靜态工廠方法會建立各種不同的對象(不同類的執行個體),而靜态工廠方法一般隻建立屬于該類的一個執行個體(包括子類);
總之,靜态工廠方法和公共構造方法都有它們的用途,并且了解它們的相對優點是值得的。通常,靜态工廠更可取,是以避免在沒有考慮靜态工廠的情況下直接選擇使用公共構造方法。
5.參考文獻
https://zhuanlan.zhihu.com/p/90837015
https://www.cnblogs.com/chenpi/p/5981084.html

本公衆号分享自己從程式員小白到經曆春招秋招斬獲10幾個offer的面試筆試經驗,其中包括【Java】、【作業系統】、【計算機網絡】、【設計模式】、【資料結構與算法】、【大廠面經】、【資料庫】期待你加入!!!
1.計算機網絡----三次握手四次揮手
2.夢想成真-----項目自我介紹
3.你們要的設計模式來了
4.一字一句教你面試“個人簡介”
5.你們要的免費書來了