Java設計模式——單例模式+工廠模式


單例模式

單例模式最初的定義出現於《設計模式》(艾迪生維斯理, 1994):“保證一個類僅有一個實例,並提供一個訪問它的全局訪問點。”
Java中單例模式定義:“一個類有且僅有一個實例,並且自行實例化向整個系統提供。”
特點:
  • 1、單例類只能有一個實例。
  • 2、單例類必須自己創建自己的唯一實例。
  • 3、單例類必須給所有其他對象提供這一實例。

Java中簡單的單例模式(不牽扯並發)注:這個是預加載,如果懶加載的話可以直接聲明變量的時候創建對象。

/**
 * 單例模式類
 * @author wanghao
 *
 */
class SinglePattern{
    private static SinglePattern instance ;
    //1.構造方法私有化
    private SinglePattern() {
    }
    //2.提供一個全局訪問點
    public static SinglePattern getInstance() {
        if(instance == null) {
            instance = new SinglePattern();
        }
        return instance;
    }
}

上面的那種方法是線程不安全的,如果並發則會創建多個對象,下面可以用一個demo測試以下,

public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(SinglePattern.getInstance());
                }
            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println(SinglePattern.getInstance());
                }
            }
        });
        t1.start();
        t2.start();
    }

創建兩個線程,線程里面分別創建對象。執行結果為:

會出現兩個對象。這樣就不符合我們的單例模式只有一個實例的思想了。

多線程有一個鎖機制。使用synchronized修飾。我們可以鎖住獲取對象的方法。代碼如下:

/**
 * 加鎖后的,單例模式類
 * @author wanghao
 *
 */
class SinglePattern{
    private static SinglePattern instance ;
    //1.構造方法私有化
    private SinglePattern() {
    }
    //2.提供一個全局訪問點 加鎖后
    public static synchronized SinglePattern getInstance() {
        if(instance == null) {
            instance = new SinglePattern();
        }
        return instance;
    }
}

然后下面我們創建的對象就只有一個實例了。每次獲取的都是一個實例。

眾所周知線程安全就等於效率會相對低點。所有經過改造優化,出現另一種雙重判斷方式。代碼如下:

/**
 * 加鎖后的,單例模式類 +優化后
 * @author wanghao
 *
 */
class SinglePattern{
    private static SinglePattern instance ;
    //1.構造方法私有化
    private SinglePattern() {
    }
    //2.提供一個全局訪問點 加鎖后
    public static  SinglePattern getInstance() {
        if(instance == null) {
            synchronized(SinglePattern.class) {
                if(instance == null) {
                    instance = new SinglePattern();
                }
            }
        }
        return instance;
    }
}

單例模式重點就一個對象,這樣理論上會是創建次數少獲取的次數多。所以,我們只需加鎖創建對象的方式,而判斷是否為null,可以不加鎖。可以提高效率。

如果按前面一種方式,如果高並發10個線程同時訪問,則需要耗費10*(等待時間+判斷時間),

而如果用第二張方式,如果通用10個線程訪問,則只需10*(判斷時間),如果沒有對象則再加上一次判斷時間和創建對象的時間。

有的人會問,上面只是為了優化, 需要兩個if判斷。我們先用一個判斷人后只需上面的代碼,驗證結果后在說原因吧。為去掉鎖里面的判斷執行結果為:

 

 兩個實例,這是為什么呢?

這是因為剛開始instance實例肯定是null的,T1和T2線程里面都是空實例,所以都過了if判斷,然后到達鎖門口,T2跑的快拿到鎖了,進去創建了個對象出去了。(注:這時候已經有對象了)然后因為T1線程已經通過判斷,到達創建對象鎖門口,等T1歸還了鎖之后,T1線程進入創建對象。然后出去。這時候兩個線程就創建了兩個對象。

所以才要在鎖里面判斷對象是否為空。

 

工廠模式

工廠模式(Factory Pattern)是 Java 中最常用的設計模式之一。這種類型的設計模式屬於創建型模式,它提供了一種創建對象的最佳方式。

把對象比作產品,創建對象的地方叫做工廠,如:Spring中的Bean和容器。

簡單工廠模式 ,代碼實現:

一個抽象類水,和三個子類,農夫三拳,哇哈哈,百歲山類。

/**
 * 我家賣水的
 * @author wanghao
 *
 */
abstract class Water{
    //賣的是可以喝的水
    public abstract void drink();
}
//農夫三拳 水
class NongFuWater extends Water{
    @Override
    public void drink() {
        System.out.println("農夫三拳有點甜。");
    }
}
//哇哈哈 水
class WaHaWater extends Water{
    @Override
    public void drink() {
        System.out.println("哇哈哈也有點甜。");
    }
}
//百歲山 水
class BaiSuiWater extends Water{
    @Override
    public void drink() {
        System.out.println("百歲山也是甜的。");
    }
}

然后創建個工廠存放我的水。進行銷售:

class WaterFactory{
    public static Water getWater(String waterName) {
        Water water = null;
        switch (waterName) {
        case "農夫三拳":
            water = new NongFuWater();
            break;
        case "哇哈哈":
            water = new WaHaWater();
            break;
        case "百歲山":
            water = new BaiSuiWater();
            break;
        }
        return water;
    }
}

然后寫測試類:

public static void main(String[] args) {
        //等待顧客買水
        Water water = null;
        System.out.println("來個農夫三拳");
        //去我工廠拿水
        water = WaterFactory.getWater("農夫三拳");
        water.drink();
        System.out.println("還渴,再來瓶哇哈哈");
        //去我工廠拿水
        water = WaterFactory.getWater("哇哈哈");
        water.drink();
        System.out.println("還渴,再來瓶百歲山");
        //去我工廠拿水
        water = WaterFactory.getWater("百歲山");
        water.drink();
        System.out.println("還渴,再來瓶洛陽宮");
        //去我工廠拿水
        water = WaterFactory.getWater("洛陽宮");
        if(water == null) {
            System.out.println("沒有該水。");
        }
    }

運行結果為:

這就是簡單的工廠模式。也叫靜態工廠。

 利用反射創建工廠對象

還有上面的例子只是把工廠換做基於反射的例子:代碼如下:

class  ReflexFactory {
    public static <T extends Water> T getWater(Class<T> clz) {
        T t = null;
        try {
            t = (T) Class.forName(clz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
        
    }
}

然后把Demo的工廠換做反射工廠。代碼如下:只寫了一個,后面略。

 

運行結果為:

看了網上之后有多種創建工廠的方法下面只列出代碼:

 多方法工廠模式,需要什么調什么

//多方法工廠模式,需要什么就調什么
class WaterFactory{
    
    public static NongFuWater getNongFuWater() {
        return new NongFuWater();
    }
    
    public static WaHaWater getWaHaWater() {
        return new WaHaWater();
    }
    
    public static BaiSuiWater getBaiSuiWater() {
        return new BaiSuiWater();
    }
}

還有java源碼的工廠模式:例如線程池都知道創建線程池子的方法有很多,其中有一種為創建固定數量的線程池方法:

/**創建具一個可重用的,有固定數量的線程池
 * 每次提交一個任務就提交一個線程,直到線程達到線城池大小,就不會創建新線程了
 * 線程池的大小達到最大后達到穩定不變,如果一個線程異常終止,則會創建新的線程
 */
        ExecutorService es=Executors.newFixedThreadPool(2);
        for(int i=0;i<10;i++){
            ThreadChi tc=new ThreadChi();
            es.execute(tc);
        }
        es.shutdown();

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM