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

 

 

 

 

posted @ 2018-05-06 00:42  苦心明  阅读(11926)  评论(0编辑  收藏  举报