工廠模式主要用一下幾種形態:java
abstract class Fruit {} class Apple extends Fruit {} class Banana extends Fruit {} class FruitFactory { public static Fruit newInstance(Class<? extends Fruit> clz) { try { return clz.newInstance(); } catch (Exception e) { throw new RuntimeException(e); } } }
每一個工廠類能夠有多於一個的工廠方法,分別負責建立不一樣的產品對象。好比java.text.DateFormat
類是其子類的工廠類,而它就提供了多個靜態工廠方法。算法
在有些狀況下,工廠角色能夠由抽象產品角色扮演。典型的應用就是java.text.DateFormat
類,一個抽象產品類同時是子類的工廠。數據庫
若是抽象產品角色已經被忽略,而工廠角色就能夠與具體產品角色合併。換言之,一個產品類爲自身的工廠。編程
class ConcreteProduct { public static ConcreteProduct factory() { return new ConcreteProduct(); } }
abstract class Fruit {} abstract class FruitFactory { public abstract Fruit newInstance(); } class Apple extends Fruit {} class Banana extends Fruit {} class AppleFactory extends FruitFactory { @Override public Fruit newInstance() { return new Apple(); } } class BananaFactory extends FruitFactory { @Override public Fruit newInstance() { return new Banana(); } }
java.util.Collection
接口繼承來Iterable
接口,全部其子類都必須實現Iterator<T> iterator()
方法,這個iterator()
方法就是一個工廠方法。緩存
抽象工廠角色和抽象產品角色均可以選擇由Java接口或者Java抽象類來實現。
若是具體工廠角色由共同的邏輯,那麼這些共同的邏輯就能夠向上移動到抽象工廠角色中,這也意味着抽象工廠角色應該由抽象類實現;反之就應當由接口實現。網絡
抽象工廠角色能夠規定出多於一個的工廠方法,從而使具體工廠角色實現這些不一樣的工廠方法。併發
工廠方法模式經常與模版方法模式一塊兒聯合使用。緣由其實不難理解:第一,兩個模式都是基於方法的,工廠方法模式是基於多態性的工廠方法的,而模版方法模式是基於模版方法和基本方法的;第二,兩個模式都將具體工做交給子類。工廠方法模式將建立工做推延給子類,模版方法模式將剩餘邏輯交給子類。app
工廠方法模式老是涉及到兩個等級結構中的對象,而這兩個等級結構能夠分別是MVC中的控制器和試圖。一個MVC模式能夠有多個控制器和多個視圖。
若是系統內只須要一個控制器,那麼能夠簡化爲簡單工廠模式。ide
享元模式使用了帶有循環邏輯的工廠方法。函數
下面例子中,手機、電腦是抽象產品,蘋果、三星等是工廠。
單例模式確保某個類只有一個實例,並且自行實例化並向整個系統提供這個實例。
私有化構造方法!
Context
,那麼容易引起內存泄漏,此時須要注意傳遞給單例對象的context
,最好是Application Context
class EagerSingleton { // 建立單例類對象 private static EagerSingleton instance = new EagerSingleton(); // 構造方法私有化 private EagerSingleton() {} // 獲取可用對象 public static EagerSingleton getInstance() { return instance; } }
class LazySingleton { // 聲明單例類對象 private static LazySingleton instance; // 構造方法私有化 private LazySingleton() {} // 獲取可用對象 public static synchronized LazySingleton getInstance() { if(null == instance) { instance = new LazySingleton(); } return instance; } }
getInstance()
方法進行了同步處理,可能會致使一些性能問題,因而有了下面的改進方法,經過雙重檢查和同步代碼塊的形式來處理懶漢式的併發問題。但要注意的是,這在Java語言中多是問題的,之因此是可能有問題,是由於不一樣Java版本的內存模型不一樣。instance
爲null
,兩個線程都經過第一次檢查instance
仍然爲null
,因而執行(5)初始化instance = new Singleton()
,而後線程1執行完畢釋放鎖。instance
不爲null
,因此線程2不會進行初始化,直接退出,返回已經初始化好的instance
。instance = new Singleton()
這一句話並非原子操做!class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() throws Exception { if(null == instance) { // (1)第一次檢查 // (2)這裏會有多個線程同時到達 synchronized(Singleton.class) { // 同步代碼塊加鎖 // (3)此處只能是單線程 if (null == instance) { // (4)第二次檢查 instance = new Singleton(); // (5)初始化instance } } } return instance; } }
爲展現問題出現的緣由,假設代碼行instance =new Singleton();
執行了下列僞代碼:
mem = allocate(); // (1)爲單例對象分配內存空間. instance = mem; // (2)注意,instance引用如今已經不是null,但還未初始化 ctorSingleton(instance); // (3)爲單例對象經過instance調用構造函數
上述僞代碼中,執行的順序多是(1)(3)(2),此時不會致使上述問題;但若是(1)(2)(3)的執行過程,則可能在線程1執行到(2)時,CPU開始執行線程2,此時剛好線程2執行到第一次檢查,獲取到的是一個不爲null
但還沒有初始化的值,此時程序會拋出錯誤。
在高版本的JDK中,使用volatile
關鍵字能夠保證不會產生上述問題。被volatile
所修飾的變量的值不會被本地線程緩存,全部對該變量的讀寫都是直接操做共享內存來實現,從而確保多個線程能正確的處理該變量。
該關鍵字可能會屏蔽掉虛擬機中的一些代碼優化,因此其運行效率可能不是很高。
class Singleton { private static volatile Singleton instance; }
class Singleton { private Singleton() {} private static class Holder { static Singleton instance = new Singleton(); } public static Singleton getInstance() { // 外圍類能直接訪問內部類(無論是不是靜態的)的私有變量 return Holder.instance; } }
多例模式實際上就是單例模式的推廣,多例類能夠有多個實例,多例類必須本身建立、管理本身的實例,並向外界提供本身的實例。
多例模式分爲有上限多例類和無上限多例類,無上限多例類要經過集合來實現。
建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象。它提供了一種建立對象的最佳方式。
一個產品一般有不一樣的組成成分做爲產品的零件,不一樣的產品能夠有不一樣的零件,建造產品的過程是建造零件的過程。建造者模式將產品的結構和產品的零件建造過程對外隱藏起來,把對建造過程進行指揮的責任和具體建造零件的責任分割開來,達到責任劃分和封裝的目的。
主要解決在開發過程當中,有時須要建立一個複雜對象,一般由多個部分的子對象構成;因爲複雜對象的多樣性,這個複雜對象的各個部分常常面臨着劇烈的變化,可是將它們組合在一塊兒的算法須要保持穩定。
public class Waiter { public static void main(String[] args) { KFCBuilder builder = new MexicanTwisterBuilder(); builder.buildBeverage(); builder.buildHamburger(); builder.buildSnack(); KFCCombo combo = builder.getCombo(); } } // 套餐接口 abstract class KFCCombo { private String hamburger; private String beverage; private String snack; // getters & setters } // 墨西哥雞肉卷套餐 class MexicanTwisterCombo extends KFCCombo {} // Builder接口 interface KFCBuilder { void buildHamburger(); void buildBeverage(); void buildSnack(); KFCCombo getCombo(); } class MexicanTwisterBuilder implements KFCBuilder { private KFCCombo combo = new MexicanTwisterCombo(); @Override public void buildHamburger() { combo.setHamburger("Mexican Twister"); } @Override public void buildBeverage() { combo.setBeverage("Pepsi Cola"); } @Override public void buildSnack() { combo.setSnack("Hot Wings"); } @Override public KFCCombo getCombo() { return combo; } }
若是一個類有不少屬性,此時爲此類寫一個Builder內部類,來輔助建造該類。
class Phone { private String screen; private String camera; private String cpu; private String battery; public static Builder builder() { return new Builder(); } public static class Builder { private Phone phone = new Phone(); public Builder screen(String screen) { phone.screen = screen; return this; } public Builder camera(String camera) { phone.camera = camera; return this; } public Builder cpu(String cpu) { phone.cpu = cpu; return this; } public Builder battery(String battery) { phone.battery = battery; return this; } public Phone build() { return phone; } } }
原始模型模式經過給一個原型對象來指明所要建立的對象的類型,而後用複製這個原型對象的辦法建立出更多同類型的對象。
這種模式涉及到三個角色:
用原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象。
clone()
方法返回的對象叫作原始對象的克隆體。一個克隆對象的基本特性必須是:
a.clone()!=a
,這也就意味着克隆對象和原始對象在java中是兩個不一樣的對象。a.clone().getClass == a.getClass()
,克隆對象與原對象類型相同a.clone.equals(a)
,也就是說克隆對象完徹底全是原始對象的一個拷貝。此條件是非必需的。Object
類沒有實現該接口,因此用戶若是沒有主動實現該接口時,調用clone()
方法會報錯CloneNotSupportedException
。
Cloneable
接口,這是步驟的關鍵之處。clone()
方法,並聲明爲public
,由於Object
的該方法是protected
的。super.clone()
來獲取新的克隆對象。在運行時刻,Object
中的clone()
識別出你要複製的是哪個對象,而後爲此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中。class A implements Cloneable { @Override public Object clone() { try { return super.clone(); } catch (CloneNotSupportedException e) { throw new RuntimeException(e); } } }
Object.clone()
是淺複製。transient
,從而將之排除在複製過程以外。public Object deepClone() throws Exception { //將對象寫到流裏 ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); //從流裏讀出來 ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); return(oi.readObject()); }