整體分爲3大類:
建立型模式 (5種):工廠方法、抽象工廠、單例、建造者、原型
結構型模式(7種):適配器、裝飾器、代理、外觀、橋接、組合、享元
行爲型模式(11種):策略、模板方法、觀察者、迭代子、責任鏈、命令、備忘錄、狀態、訪問者、中介者、解釋器
其它(2種):併發型、線程池java
建立1個接口、2個實現類編程
public interface Sender { void send(); }
public class MailSender implements Sender { @Override public void send() { System.out.println("this is mailsender!"); } }
public class SmsSender implements Sender { @Override public void send() { System.out.println("this is smssender!"); } }
建立1個工廠接口、2個工廠實現類segmentfault
public interface Provider { Sender produce(); }
public class SendMailFactory implements Provider { @Override public Sender produce() { return new MailSender(); } }
public class SendSmsFactory implements Provider { @Override public Sender produce() { return new SmsSender(); } }
測試類設計模式
public class Test { public static void main(String[] args) { Provider provider = new SendMailFactory(); Sender sender = provider.produce(); sender.send(); } }
若是想增長一個功能,則只需作一個實現類,實現 Sender 接口,同時作一個工廠類,實現 Provider 接口,就 OK 了,無需去改動現成的代碼。這樣作,拓展性較好!多線程
工廠方法模式和抽象工廠模式的區別以下:
工廠方法模式:併發
抽象工廠模式:ide
對於 java 來講,你能見到的大部分抽象工廠模式都是這樣的:
---它的裏面是一堆工廠方法,每一個工廠方法返回某種類型的對象。
好比說工廠能夠生產鼠標和鍵盤。那麼抽象工廠的實現類(它的某個具體子類)的對象均可以生產鼠標和鍵盤,但可能工廠 A 生產的是羅技的鍵盤和鼠標,工廠 B 是微軟的。
用了工廠方法模式,你替換生成鍵盤的工廠方法,就能夠把鍵盤從羅技換到微軟。可是用了抽象工廠模式,你只要換家工廠,就能夠同時替換鼠標和鍵盤一套。若是你要的產品有幾十個,固然用抽象工廠模式一次替換所有最方便(這個工廠會替你用相應的工廠方法)因此說抽象工廠就像工廠,而工廠方法則像是工廠的一種產品生產線性能
單例模式能保證在一個JVM中該對象只有一個實例存在。好處:
一、 避免頻繁建立對象,節省系統開銷,減輕 GC 壓力。
二、在系統中某些對象只能有一個(好比一個軍隊出現了多個司令員同時指揮,確定會亂成一團)測試
簡單的單例類(單線程):
只能在單線程中用,不能用於多線程。優化
public class Singleton { /* 持有私有靜態實例,防止被引用,此處賦值爲 null,目的是實現延遲加載 */ private static Singleton instance = null; /* 私有構造方法,防止被實例化 */ private Singleton() { } /* 靜態工程方法,建立實例 */ public static Singleton getInstance() { if (instance == null){ instance = new Singleton(); } return instance; } /* 若是該對象被用於序列化,能夠保證對象在序列化先後保持一致 */ public Object readResolve(){ return instance; } }
同步代碼塊:
/* 靜態工程方法,建立實例 */ public static Singleton getInstance() { if (instance == null){ synchronized (instance){ if(instance == null){ instance = new Singleton(); } } } return instance; }
在 Java 指令中建立對象和賦值操做是分開進行的,也就是說 instance = new Singleton();語句是分兩步執行的,可能會先爲Singleton實例分配空間,再賦值給instance,最後初始化Singleton實例。
A、B 兩個線程爲例:
多線程單例
使用內部類來維護單例的實現,JVM 內部的機制可以保證當一個類被加載的時候,這個類的加載過程是線程互斥的。這樣當咱們第一次調用 getInstance 的時候,JVM 可以幫咱們保證 instance 只被建立一次,而且會保證把賦值給 instance 的內存初始化完畢,這樣咱們就不用擔憂上面的問題。同時該方法也只會在第一次調用的時候使用互斥機制,這樣就解決了低性能問題。
public class Singleton { /* 私有構造方法,防止被實例化 */ private Singleton() { } /* 此處使用一個內部類來維護單例 */ private static class SingletonFactory{ private static Singleton instance = new Singleton(); } /* 獲取實例 */ public static Singleton getInstance(){ return SingletonFactory.instance; } /* 若是該對象被用於序列化,能夠保證對象在序列化先後保持一致 */ public Object readResolve(){ return getInstance(); } }
將建立和賦值分開,單獨爲建立類加靜態同步方法
由於咱們只須要在建立類的時候進行同步,因此只要將建立和getInstance()分開,單獨爲建立加 synchronized 關鍵字,也是能夠的
public class SingletonTest { private static SingletonTest instance = null; public SingletonTest() { } private static synchronized void syncInit(){ if(instance == null){ instance = new SingletonTest(); } } public static SingletonTest getInstance() { if (instance == null){ syncInit(); } return instance; } }
補充:用 採用" 影子實例"的辦法爲單例對象的屬性同步更新
public class SingletonTest { private static SingletonTest instance = null; private Vector properties = null; public Vector getProperties() { return properties; } public SingletonTest() { } private static synchronized void syncInit(){ if(instance == null){ instance = new SingletonTest(); } } public static SingletonTest getInstance() { if (instance == null){ syncInit(); } return instance; } public void updateProperties(){ SingletonTest shadow = new SingletonTest(); properties = shadow.getProperties(); } }
類和靜態方法與靜態類區別:
將一個對象做爲原型,對其進行復制、克隆後產生一個和原對象相似的新對象
淺複製:將一個對象複製後,基本數據類型的變量都會從新建立,而引用類型,指向的仍是原對象所指向的。
深複製:將一個對象複製後,不管是基本數據類型還有引用類型,都是從新建立的。
一個原型類,只須要實現 Cloneable 接口,覆寫 clone 方法,此處 clone 方法能夠改爲任意的名稱,由於 Cloneable 接口是個空接口,你能夠任意定義實現類的方法名,如 cloneA或者cloneB,由於此處的重點是super.clone()這句話,super.clone()調用的是Object的clone()方法,而在 Object 類中,clone()是 native 的。這裏寫一個深淺複製的例子
public class Prototype implements Cloneable, Serializable { private static final long serialVersionUID = 1L; private String string; private SerializableObject obj; /*淺複製*/ public Object clone() throws CloneNotSupportedException{ Prototype proto = (Prototype)super.clone(); return proto; } /*深複製*/ public Object deepClone() throws IOException, ClassNotFoundException { /* 寫入當前對象的二進制流 */ ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); /* 讀出二進制流產生的新對象 */ ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } public String getString() { return string; } public void setString(String string) { this.string = string; } public SerializableObject getObj() { return obj; } public void setObj(SerializableObject obj) { this.obj = obj; } } class SerializableObject implements Serializable{ private static final long serialVersionUID = 1L; }