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