Intent
確保一個類只有一個實例,並提供該實例的全局訪問點。java
Class Diagram
使用一個私有構造函數、一個私有靜態變量以及一個公有靜態函數來實現。編程
私有構造函數保證了不能經過構造函數來建立對象實例,只能經過公有靜態函數返回惟一的私有靜態變量。緩存
Implementation
Ⅰ 懶漢式-線程不安全
如下實現中,私有靜態變量 uniqueInstance 被延遲實例化,這樣作的好處是,若是沒有用到該類,那麼就不會實例化 uniqueInstance,從而節約資源。安全
這個實如今多線程環境下是不安全的,若是多個線程可以同時進入 if (uniqueInstance == null) ,而且此時 uniqueInstance 爲 null,那麼會有多個線程執行 uniqueInstance = new Singleton(); 語句,這將致使實例化屢次 uniqueInstance。多線程
public class Singleton {併發
private static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; }
}
Ⅱ 餓漢式-線程安全
線程不安全問題主要是因爲 uniqueInstance 被實例化屢次,採起直接實例化 uniqueInstance 的方式就不會產生線程不安全問題。app
可是直接實例化的方式也丟失了延遲實例化帶來的節約資源的好處。分佈式
public class Singleton {ide
//線程不安全問題主要是因爲 uniqueIntance被實例化了屢次, //若是uniqueInstance採用直接實例化的話,就不會被實例化屢次,也就不會產生線程不安全的問題。 private static Singleton uniqueInstance=new Singleton2(); private Singleton(){ } public static Singleton getUniqueInstance(){ return uniqueInstance; }
}
Ⅲ 懶漢式-線程安全
只須要對 getUniqueInstance() 方法加鎖,那麼在一個時間點只能有一個線程可以進入該方法, 從而避免了實例化屢次 uniqueInstance。函數
可是當一個線程進入該方法以後,其它試圖進入該方法的線程都必須等待, 即便 uniqueInstance 已經被實例化了。這會讓線程阻塞時間過長,所以該方法有性能問題,不推薦使用。
public class Singleton{
//線程不安全問題主要是因爲 uniqueIntance被實例化了屢次, //若是uniqueInstance採用直接實例化的話,就不會被實例化屢次,也就不會產生線程不安全的問題。 private static Singleton uniqueInstance; private Singleton(){ } //當一個線程進入該方法以後,其它試圖進入該方法的線程都必須等待 public synchronized static Singleton getUniqueInstance(){ if(uniqueInstance == null){ uniqueInstance = new Singleton(); } return uniqueInstance; }
}
延遲加載的思想 就是一開始不要加載資源或者數據,一直等,等到立刻就要使用這個資源或者數據了, 躲不過去了才加載,因此也稱Lazy Load, 不是懶惰啊,是「延遲加載」,這在實際開發中是一種很常見的思想,儘量的節約資源。
緩存的思想 把這些數據緩存到內存裏面,每次操做的時候,先到內存裏面找,看有沒有這些數據, 若是有,那麼就直接使用,若是沒有那麼就獲取它,並設置到緩存中,下一次訪問的時候就能夠直接從內存中獲取了。 從而節省大量的時間,固然,緩存是一種典型的空間換時間的方案。
Ⅳ 雙重校驗鎖-線程安全
uniqueInstance 只須要被實例化一次,以後就能夠直接使用了。加鎖操做只須要對實例化那部分的代碼進行,只有當 uniqueInstance 沒有被實例化時,才須要進行加鎖。
雙重校驗鎖先判斷 uniqueInstance 是否已經被實例化,若是沒有被實例化,那麼纔對實例化語句進行加鎖。
public class Singleton {
private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqueInstance() { if (uniqueInstance == null) { synchronized (Singleton.class) { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } } } return uniqueInstance; }
}
考慮下面的實現,也就是隻使用了一個 if 語句。 在 uniqueInstance == null 的狀況下,若是兩個線程都執行了 if 語句,那麼兩個線程都會進入 if 語句塊內。雖然在 if 語句塊內有加鎖操做,可是兩個線程都會執行 uniqueInstance = new Singleton(); 這條語句,只是前後的問題,那麼就會進行兩次實例化。所以必須使用雙重校驗鎖,也就是須要使用兩個 if 語句。
if (uniqueInstance == null) {
synchronized (Singleton.class) { uniqueInstance = new Singleton(); }
}
uniqueInstance 採用 volatile 關鍵字修飾也是頗有必要的, uniqueInstance = new Singleton(); 這段代碼實際上是分爲三步執行:
爲 uniqueInstance 分配內存空間
初始化 uniqueInstance
將 uniqueInstance 指向分配的內存地址
可是因爲 JVM 具備指令重排的特性,執行順序有可能變成 1>3>2。指令重排在單線程環境下不會出現問題,可是在多線程環境下會致使一個線程得到尚未初始化的實例。 例如,線程 T1 執行了 1 和 3,此時 T2 調用 getUniqueInstance() 後發現 uniqueInstance 不爲空,所以返回 uniqueInstance,但此時 uniqueInstance 還未被初始化。
使用 volatile 能夠禁止 JVM 的指令重排,保證在多線程環境下也能正常運行。
Ⅴ 靜態內部類實現
當 Singleton 類加載時,靜態內部類 SingletonHolder 沒有被加載進內存。只有當調用 getUniqueInstance() 方法從而觸發 SingletonHolder.INSTANCE 時 SingletonHolder 纔會被加載,此時初始化 INSTANCE 實例,而且 JVM 能確保 INSTANCE 只被實例化一次。
這種方式不只具備延遲初始化的好處,並且由 JVM 提供了對線程安全的支持。
public class Singleton {
private Singleton() { } private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } public static Singleton getUniqueInstance() { return SingletonHolder.INSTANCE; }
}
Ⅵ 枚舉實現
public enum Singleton {
INSTANCE; private String objName; public String getObjName() { return objName; } public void setObjName(String objName) { this.objName = objName; } public static void main(String[] args) { // 單例測試 Singleton firstSingleton = Singleton.INSTANCE; firstSingleton.setObjName("firstName"); System.out.println(firstSingleton.getObjName()); Singleton secondSingleton = Singleton.INSTANCE; secondSingleton.setObjName("secondName"); System.out.println(firstSingleton.getObjName()); System.out.println(secondSingleton.getObjName()); // 反射獲取實例測試 try { Singleton[] enumConstants = Singleton.class.getEnumConstants(); for (Singleton enumConstant : enumConstants) { System.out.println(enumConstant.getObjName()); } } catch (Exception e) { e.printStackTrace(); } }
}
firstName
secondName
secondName
secondName
該實如今屢次序列化再進行反序列化以後,不會獲得多個實例。而其它實現須要使用 transient 修飾全部字段, 而且實現序列化和反序列化的方法。
該實現能夠防止反射攻擊。在其它實現中,經過 setAccessible() 方法能夠將私有構造函數的訪問級別設置爲 public,而後調用構造函數從而實例化對象,若是要防止這種攻擊,須要在構造函數中添加防止屢次實例化的代碼。該實現是由 JVM 保證只會實例化一次,所以不會出現上述的反射攻擊。
枚舉實現(最推薦使用)
public class Singleton {
private Singleton(){} public static Singleton getUniqueInstance(){ return Singleton.INSTANCE.getSingleton(); } private enum Singleton{ INSTANCE; //若是打算自定義本身的方法,那麼必須在enum實例序列的最後添加一個分號。 //並且 Java 要求必須先定義 enum 實例 private Singleton singleton; //JVM保證這個方法絕對只被調用一次 Singleton(){ singleton=new Singleton(); } public Singleton getSingleton() { return singleton; } }
}
Examples
Logger Classes
Configuration Classes
Accesing resources in shared mode
Factories implemented as Singletons
JDK
java.lang.Runtime#getRuntime()
java.awt.Desktop#getDesktop()
java.lang.System#getSecurityManager()
Intent
在建立一個對象時不向客戶暴露內部細節,並提供一個建立對象的通用接口。
Class Diagram
簡單工廠把實例化的操做單獨放到一個類中,這個類就成爲簡單工廠類, 讓簡單工廠類來決定應該用哪一個具體子類來實例化。
這樣作能把客戶類和具體子類的實現解耦, 客戶類再也不須要知道有哪些子類以及應當實例化哪一個子類。 客戶類每每有多個,若是不使用簡單工廠,那麼全部的客戶類都要知道全部子類的細節。 並且一旦子類發生改變,例如增長子類,那麼全部的客戶類都要進行修改。
Implementation
public interface Product {
}
public class ConcreteProduct implements Product {
}
public class ConcreteProduct1 implements Product {
}
public class ConcreteProduct2 implements Product {
}
如下的 Client 類包含了實例化的代碼,這是一種錯誤的實現。若是在客戶類中存在這種實例化代碼,就須要考慮將代碼放到簡單工廠中。
public class Client {
public static void main(String[] args) { int type = 1; Product product; if (type == 1) { product = new ConcreteProduct1(); } else if (type == 2) { product = new ConcreteProduct2(); } else { product = new ConcreteProduct(); } // do something with the product }
}
如下的 SimpleFactory 是簡單工廠實現,它被全部須要進行實例化的客戶類調用。
public class SimpleFactory {
public Product createProduct(int type) { if (type == 1) { return new ConcreteProduct1(); } else if (type == 2) { return new ConcreteProduct2(); } return new ConcreteProduct(); }
}
public class Client {
public static void main(String[] args) { SimpleFactory simpleFactory = new SimpleFactory(); Product product = simpleFactory.createProduct(1); // do something with the product }
}
簡單工廠的優缺點
幫助封裝:簡單工廠雖然很簡單,可是很是友好的幫助咱們實現了組件的封裝,而後讓組件外部能真正面向接口編程。
解耦:經過簡單工廠,把客戶類和具體子類的實現解耦。
可能增長客戶端的複雜度: 若是經過客戶端的參數來選擇具體的實現類, 那麼就必須讓客戶端能理解各個參數所表明的具體功能和含義,這會增長客戶端使用的難度, 也部分暴露了內部實現,這種狀況能夠選用可配置的方式來實現
不方便擴展子工廠:私有化簡單工廠的構造方法,使用靜態方法來建立接口, 也就不能經過寫簡單工廠類的子類來改變建立接口的方法的行爲了。不過,一般狀況下是不須要爲簡單工廠建立子類的。
Intent
定義了一個建立對象的接口,但由子類決定要實例化哪一個類。工廠方法把實例化操做推遲到子類。
Class Diagram
在簡單工廠中,建立對象的是另外一個類,而在工廠方法中,是由子類來建立對象。
下圖中,Factory 有一個 doSomething() 方法,這個方法須要用到一個產品對象,這個產品對象由 factoryMethod() 方法建立。該方法是抽象的,須要由子類去實現。
Implementation
public abstract class Factory {
abstract public Product factoryMethod(); public void doSomething() { Product product = factoryMethod(); // do something with the product }
}
public class ConcreteFactory extends Factory {
public Product factoryMethod() { return new ConcreteProduct(); }
}
public class ConcreteFactory1 extends Factory {
public Product factoryMethod() { return new ConcreteProduct1(); }
}
public class ConcreteFactory2 extends Factory {
public Product factoryMethod() { return new ConcreteProduct2(); }
}
JDK
java.util.Calendar
java.util.ResourceBundle
java.text.NumberFormat
java.nio.charset.Charset
java.net.URLStreamHandlerFactory
java.util.EnumSet
javax.xml.bind.JAXBContext
Intent
提供一個接口,用於建立 相關的對象家族 。
Class Diagram
抽象工廠模式建立的是對象家族,也就是不少對象而不是一個對象,而且這些對象是相關的,也就是說必須一塊兒建立出來。而工廠方法模式只是用於建立一個對象,這和抽象工廠模式有很大不一樣。
抽象工廠模式用到了工廠方法模式來建立單一對象,AbstractFactory 中的 createProductA() 和 createProductB() 方法都是讓子類來實現,這兩個方法單獨來看就是在建立一個對象,這符合工廠方法模式的定義。
至於建立對象的家族這一律念是在 Client 體現,Client 要經過 AbstractFactory 同時調用兩個方法來建立出兩個對象,在這裏這兩個對象就有很大的相關性,Client 須要同時建立出這兩個對象。
從高層次來看,抽象工廠使用了組合,即 Cilent 組合了 AbstractFactory,而工廠方法模式使用了繼承。
Implementation
public class AbstractProductA {
}
public class AbstractProductB {
}
public class ProductA1 extends AbstractProductA {
}
public class ProductA2 extends AbstractProductA {
}
public class ProductB1 extends AbstractProductB {
}
public class ProductB2 extends AbstractProductB {
}
public abstract class AbstractFactory {
abstract AbstractProductA createProductA(); abstract AbstractProductB createProductB();
}
public class ConcreteFactory1 extends AbstractFactory {
AbstractProductA createProductA() { return new ProductA1(); } AbstractProductB createProductB() { return new ProductB1(); }
}
public class ConcreteFactory2 extends AbstractFactory {
AbstractProductA createProductA() { return new ProductA2(); } AbstractProductB createProductB() { return new ProductB2(); }
}
public class Client {
public static void main(String[] args) { AbstractFactory abstractFactory = new ConcreteFactory1(); AbstractProductA productA = abstractFactory.createProductA(); AbstractProductB productB = abstractFactory.createProductB(); // do something with productA and productB }
}
JDK
javax.xml.parsers.DocumentBuilderFactory
javax.xml.transform.TransformerFactory
javax.xml.xpath.XPathFactory
Intent
封裝一個對象的構造過程,並容許按步驟構造。 (將一個複雜對象的構建與它的表示分離,使得一樣的構建過程能夠建立不一樣的表示。)
Class Diagram
要實現一樣的構建過程能夠建立不一樣的表現,那麼一個天然的思路就是 先把構建過程獨立出來,在生成器模式中把它稱爲指導者, 由它來指導裝配過程,可是不負責每步具體的實現。 固然,光有指導者是不夠的,必需要有能具體實現每步的對象,在生成器模式中稱這些實現對象爲生成器。 這樣一來,指導者就是能夠重用的構建過程,而生成器是能夠被切換的具體實現。
Implementation1
**
*/
public class Director {
private AbstractComputerBuilder computerBuilder; public void setComputerBuilder(AbstractComputerBuilder computerBuilder) { this.computerBuilder = computerBuilder; } public Product getProduct() { return computerBuilder.getProduct(); } public void constructComputer() { computerBuilder.buildProduct(); computerBuilder.buildMaster(); computerBuilder.buildScreen(); computerBuilder.buildKeyboard(); computerBuilder.buildMouse(); computerBuilder.buildAudio(); }
}
/**
*/
public class Product {
private String master; private String screen; private String keyboard; private String mouse; private String audio; public void setMaster(String master) { this.master = master; } public void setScreen(String screen) { this.screen = screen; } public String getMaster() { return master; } public String getScreen() { return screen; } public String getKeyboard() { return keyboard; } public void setKeyboard(String keyboard) { this.keyboard = keyboard; } public String getMouse() { return mouse; } public void setMouse(String mouse) { this.mouse = mouse; } public String getAudio() { return audio; } public void setAudio(String audio) { this.audio = audio; }
}
/**
*/
public abstract class AbstractComputerBuilder {
protected Product product; public Product getProduct() { return product; } public void buildProduct(){ product=new Product(); System.out.println("生產出一臺電腦"); } public abstract void buildMaster(); public abstract void buildScreen(); public abstract void buildKeyboard(); public abstract void buildMouse(); public abstract void buildAudio();
}
public class HPComputerBuilder extends AbstractComputerBuilder{
@Override public void buildMaster() { // TODO Auto-generated method stub product.setMaster("i7,16g,512SSD,1060"); System.out.println("(i7,16g,512SSD,1060)的惠普主機"); } @Override public void buildScreen() { // TODO Auto-generated method stub product.setScreen("4K"); System.out.println("(4K)的惠普顯示屏"); } @Override public void buildKeyboard() { // TODO Auto-generated method stub product.setKeyboard("cherry 青軸機械鍵盤"); System.out.println("(cherry 青軸機械鍵盤)的鍵盤"); } @Override public void buildMouse() { // TODO Auto-generated method stub product.setMouse("MI 鼠標"); System.out.println("(MI 鼠標)的鼠標"); } @Override public void buildAudio() { // TODO Auto-generated method stub product.setAudio("飛利浦 音響"); System.out.println("(飛利浦 音響)的音響"); }
}
public class DELLComputerBuilder extends AbstractComputerBuilder{
@Override public void buildMaster() { // TODO Auto-generated method stub product.setMaster("i7,32g,1TSSD,1060"); System.out.println("(i7,32g,1TSSD,1060)的戴爾主機"); } @Override public void buildScreen() { // TODO Auto-generated method stub product.setScreen("4k"); System.out.println("(4k)的dell顯示屏"); } @Override public void buildKeyboard() { // TODO Auto-generated method stub product.setKeyboard("cherry 黑軸機械鍵盤"); System.out.println("(cherry 黑軸機械鍵盤)的鍵盤"); } @Override public void buildMouse() { // TODO Auto-generated method stub product.setMouse("MI 鼠標"); System.out.println("(MI 鼠標)的鼠標"); } @Override public void buildAudio() { // TODO Auto-generated method stub product.setAudio("飛利浦 音響"); System.out.println("(飛利浦 音響)的音響"); }
}
/**
*/
public class Client {
public static void main(String[] args) { AbstractComputerBuilder computerBuilder=new HPComputerBuilder(); AbstractComputerBuilder computerBuilder2=new DELLComputerBuilder(); Director director=new Director(); director.setComputerBuilder(computerBuilder); director.constructComputer(); //獲取PC Product pc=director.getProduct(); director.setComputerBuilder(computerBuilder2); director.constructComputer(); Product pc2=director.getProduct(); }
}
生成器的調用順序:
Implementation2
如下是一個簡易的 StringBuilder 實現,參考了 JDK 1.8 源碼。
public class AbstractStringBuilder {
protected char[] value; protected int count; public AbstractStringBuilder(int capacity) { count = 0; value = new char[capacity]; } public AbstractStringBuilder append(char c) { ensureCapacityInternal(count + 1); value[count++] = c; return this; } private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity); } void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity); }
}
public class StringBuilder extends AbstractStringBuilder {
public StringBuilder() { super(16); } @Override public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
}
public class Client {
public static void main(String[] args) { StringBuilder sb = new StringBuilder(); final int count = 26; for (int i = 0; i < count; i++) { sb.append((char) ('a' + i)); } System.out.println(sb.toString()); }
}
abcdefghijklmnopqrstuvwxyz
JDK
java.lang.StringBuilder
java.nio.ByteBuffer
java.lang.StringBuffer
java.lang.Appendable
Apache Camel builders
Intent
使用原型實例指定要建立對象的類型,經過複製這個原型來建立新對象。
Class Diagram
Implementation
public abstract class Prototype {
abstract Prototype myClone();
}
public class ConcretePrototype extends Prototype {
private String filed; public ConcretePrototype(String filed) { this.filed = filed; } @Override Prototype myClone() { return new ConcretePrototype(filed); } @Override public String toString() { return filed; }
}
public class Client {
public static void main(String[] args) { Prototype prototype = new ConcretePrototype("abc"); Prototype clone = prototype.myClone(); System.out.println(clone.toString()); }
}
abc
免費Java高級資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分佈式等教程,一共30G。
傳送門:https://mp.weixin.qq.com/s/Jz...