Java內功心法,建立型設計模式包括哪些

  1. 單例(Singleton)

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()

  1. 簡單工廠(Simple Factory)

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
}

}
簡單工廠的優缺點
幫助封裝:簡單工廠雖然很簡單,可是很是友好的幫助咱們實現了組件的封裝,而後讓組件外部能真正面向接口編程。
解耦:經過簡單工廠,把客戶類和具體子類的實現解耦。
可能增長客戶端的複雜度: 若是經過客戶端的參數來選擇具體的實現類, 那麼就必須讓客戶端能理解各個參數所表明的具體功能和含義,這會增長客戶端使用的難度, 也部分暴露了內部實現,這種狀況能夠選用可配置的方式來實現
不方便擴展子工廠:私有化簡單工廠的構造方法,使用靜態方法來建立接口, 也就不能經過寫簡單工廠類的子類來改變建立接口的方法的行爲了。不過,一般狀況下是不須要爲簡單工廠建立子類的。

  1. 工廠方法(Factory Method)

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

  1. 抽象工廠(Abstract Factory)

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

  1. 生成器(Builder)

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

  1. 原型模式(Prototype)

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...

相關文章
相關標籤/搜索