1. 程式人生 > >單例模式,工廠模式,代理模式彙總

單例模式,工廠模式,代理模式彙總

  • 1.單例模式:
    餓漢式 (可用) public class Demo{
    private static Demo demo = new Demo();
    private Demo(){
    }
    public static Demo getInstance(){
    return demo;
    }
    }
    懶漢式雙鎖(不可用) public class Demo{
    private static Demo demo;
    private Demo(){
    }
    public static Demo getInstance(){
    if(demo==null){
    synchronized(this)
    if(demo==null){
    demo = new Demo();
    }
    }
    return demo;
    }
    }

bug:
缺陷:instance=new Singleton();
它不是一個原子操作,這句話做了3件事:
1.給Singleton的例項分配記憶體
2.初始化Sinhleton的構造器
3.將instance物件指向分配的記憶體空間(這時instance就非null了)
由於Java編譯器允許處理器亂序執行,上面的順序可能是1-2-3,也可能是1-3-2,這樣就會出現問題。
原子操作:這種操作一旦開始,就一直執行到結束,中間不會切換到另一個執行緒
補充:在Java中設定變數值的操作,除了long和double型別的變數外都是原子操作

在J2SE 5.0中,這一問題被修正了。volatile關鍵字保證多個執行緒可以正確處理單件例項
volatile確保本條指令不會被編譯器優化,且要求每次直接讀值
1)它確保指令重排序時不會把其後面的指令排到記憶體屏障之前的位置,也不會把前面的指令排到記憶體屏障的後面;即在執行到記憶體屏障這句指令時,在它前面的操作已經全部完成;
2)它會強制將對快取的修改操作立即寫入主存,對volatile變數所有的寫操作都能立刻被其他執行緒得知,保證此變數對所有執行緒的可見性,但是這並不代表基於volatile變數的運算在併發下是安全的,因為volatile只能保證記憶體可見性,卻沒有保證對變數操作的原子性。
3)如果是寫操作,它會導致其他CPU中對應的快取行無效。
volatile和synchronized:
volatile本質是在告訴jvm當前變數在暫存器中的值是不確定的,需要從主存中讀取,synchronized則是鎖定當前變數,只有當前執行緒可以訪問該變數,其他執行緒被阻塞住.
volatile僅能使用在變數級別,synchronized則可以使用在變數,方法.
volatile僅能實現變數的修改可見性和有序性,但不具備原子特性,而synchronized則可以保證變數的修改可見性和原子性,有序性
volatile不會造成執行緒的阻塞,而synchronized可能會造成執行緒的阻塞.
volatile標記的變數不會被編譯器優化,一定程度上保證有序性,而synchronized標記的變數可以被編譯器優化.
volatile比普通變數開銷大,它需要插入記憶體屏障指令來保證處理器不會發生亂序執行,比synchronized開銷小
推薦用:
public class Demo{
private static volatile Demo demo;
private Demo(){
}
public static Demo getInstance(){
if(demo==null){
synchronized(this)
if(demo==null){
demo = new Demo();
}
}
return demo;
}
}
Universal-Image-Loader和Eventbus就是通過此種方式來建立單例

靜態內部類(推薦用)
public class Demo{
private Demo(){
}
private static class DemoInstance(){
private static final Demo demo = new Demo();
}

public static Demo getInstance(){
return DemoInstance.demo;
}
}
列舉(推薦用)
enum裡面的列舉資料都是執行緒安全的,而enum實現的單例又是最簡單的,Stackoverflow上最贊同的,也是Effective Java的作者推薦的方式

  • 2.工廠模式
    將具體建立產品例項的過程封裝在工廠類中,實現客戶端和建立產品例項的解耦,讓客戶端只負責消費,滿足java設計原則的單一職責原則和麵向物件的封裝性(不用到處new例項)

簡單工廠 :用來生產同一等級結構中的產品(不方便增加新產品,不修改程式碼無法擴充套件)。
工廠方法 :用來生產同一等級結構中的產品(支援增加新產品)。
抽象工廠:用來生產不同產品族的全部產品。(支援增加產品族)。
這裡寫圖片描述

這裡寫圖片描述

這裡寫圖片描述

  • 3.代理模式
    概念:為其他物件提供一種代理以控制對這個物件的訪問。在某些情況下,一個物件不適合或者不能直接引用另一個物件,而代理物件可以在客戶端和目標物件之間起到中介的作用。代理模式其實就是在訪問物件時引入一定程度的間接性,因為這種間接性可以附加多種用途。

優點(我們為什麼要用代理模式):
代理類可以在執行真實物件操作時,附加其他的操作,比如許可權控制,相當於對真實物件進行封裝,同時也能協調好呼叫者與被呼叫者,降低系統耦合性。
1.虛擬代理,根據需要建立開銷很大的物件,通過它來存放例項化需要很長時間的真實物件。(大圖片的預覽圖)
2.安全代理,用來控制真實物件訪問時的許可權。(會員和遊客)
3.智慧指引,是指當呼叫真實物件時,代理處理另外一些事。(之前做什麼,之後做什麼)
動態代理:反射。AOP(代理物件不明確)
Proxy類static Object newProxyInstance(ClassLoader loader,Class[ ] interfaces,InvocationHandler h):
loader:定義由哪個classloader物件來對生成的代理物件進行載入
interfaces:一個Interface物件的陣列,表示的是我將要給我需要代理的物件提供一組什麼介面,如果我提供了一組介面給它,那麼這個代理物件就宣稱實現了該介面這樣我就能呼叫這組介面中的方法了
h:一個InvocationHandler物件,表示的是當我這個動態代理物件在呼叫方法的時候,會關聯到哪一個InvocationHandler物件的invoke方法上
public Object invoke(Object proxy,Method method,Object[ ]args) throws Throwable
{
System.out.println(“before calling”);
Object result=method.invoke(sub,args);
System.out.println(“after calling”);
return result;
}

AOP和OOP區別:
AOP面向切面,橫向,從左到右;OOP面向物件,縱向,從上到下