Android设计模式之单例模式
公司组织技术沙龙,需要讲到关于设计模式的内容,在这里做一下笔录
首先讲的是单例模式,
单例模式: 一个类有且仅有一个实例,并且自行实例化向整个系统提供入口
从定义上来看,其实看起来简单,但稍微会有点懵逼,我们待会从实际代码中去自行体会吧。。
先来了解单例模式的一些特点:
1.一个类只能有一个实例
2.自己创建这个实例
3.整个系统都使用这一个实例
优势:
1.能够避免实例重复创建
2.能避免存在多个实例引起程序逻辑错误的场合
3.节约内存
我们先使用普通实例化的方式进行编程:
1 package com.oysd.singleton; 2 3 /** 4 * @author ouyangshengduo 5 * @description 不用单例模式运行的效果 6 */ 7 8 //仓库类 9 class StoreHouse { 10 private int quantity = 100; 11 12 public void setQuantity(int quantity) { 13 this.quantity = quantity; 14 } 15 16 public int getQuantity() { 17 return quantity; 18 } 19 } 20 21 //搬货工人类 22 class Carrier{ 23 public StoreHouse mStoreHouse; 24 public Carrier(StoreHouse storeHouse){ 25 mStoreHouse = storeHouse; 26 } 27 //搬货进仓库 28 public void MoveIn(int i){ 29 mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i); 30 } 31 //搬货出仓库 32 public void MoveOut(int i){ 33 mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i); 34 } 35 } 36 37 //工人搬运测试 38 public class SinglePattern { 39 public static void main(String[] args){ 40 StoreHouse mStoreHouse1 = new StoreHouse(); 41 StoreHouse mStoreHouse2 = new StoreHouse(); 42 Carrier Carrier1 = new Carrier(mStoreHouse1); 43 Carrier Carrier2 = new Carrier(mStoreHouse2); 44 45 System.out.println("两个是不是同一个?"); 46 47 if(mStoreHouse1.equals(mStoreHouse2)){//这里用equals而不是用 == 符号,因为 == 符号只是比较两个对象的地址 48 System.out.println("是同一个"); 49 }else { 50 System.out.println("不是同一个"); 51 } 52 //搬运工搬完货物之后出来汇报仓库商品数量 53 Carrier1.MoveIn(30); 54 System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity()); 55 Carrier2.MoveOut(50); 56 System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity()); 57 } 58 }
运行结果:
1 两个是不是同一个? 2 不是同一个 3 仓库商品余量:130 4 仓库商品余量:50
以上出现了一些问题,两个实例不是同一个实例,造成了浪费,数据出现了矛盾,没有同步
故而知单例存在的意义
单例模式虽然简单,但功能还是比较强大的,他有好多种的编写方式,在此我暂时列举五种方式:
第一种:只适用于单线程环境,一般用的比较少
1 package com.oysd.singleton; 2 /** 3 * @author ouyangshengduo 4 * @describe Singleton的静态属性instance中,只有instance为 5 * null的时候才创建一个实例,构造函数私有,确保每次都只创建一个,避 6 * 免重复创建。 7 * 缺点:只在单线程的情况下正常运行,在多线程的情况下,就会出问题。 8 * 例如:当两个线程同时运行到判断instance是否为空的if语句,并且 9 * instance确实没有创建好时,那么两个线程都会创建一个实例。 10 */ 11 public class Singleton { 12 private static Singleton instance=null; 13 private Singleton(){ 14 15 } 16 public static Singleton getInstance(){ 17 if(instance==null){ 18 instance=new Singleton(); 19 } 20 return instance; 21 } 22 }
第二种:懒汉式,感觉不太好,因为整个方法都加上锁很耗资源
1 package com.oysd.singleton; 2 /** 3 * @author ouyangshengduo 4 * @descripbe 在解法一的基础上加上了同步锁,使得在多线程的情况下可 5 * 以用。例如:当两个线程同时想创建实例,由于在一个时刻只有一个线程 6 * 能得到同步锁,当第一个线程加上锁以后,第二个线程只能等待。第一个 7 * 线程发现实例没有创建,创建之。第一个线程释放同步锁,第二个线程才 8 * 可以加上同步锁,执行下面的代码。由于第一个线程已经创建了实例,所 9 * 以第二个线程不需要创建实例。保证在多线程的环境下也只有一个实例。 10 * 缺点:每次通过getInstance方法得到singleton实例的时候都有一个试 11 * 图去获取同步锁的过程。而众所周知,加锁是很耗时的。能避免则避免。 12 */ 13 public class Singleton { 14 private static Singleton instance=null; 15 private Singleton(){ 16 17 } 18 public static synchronized Singleton getInstance(){ 19 if(instance==null){ 20 instance=new Singleton(); 21 } 22 return instance; 23 } 24 }
第三种:加同步锁,双重检查,建议使用
1 package com.oysd.singleton; 2 /** 3 * @author ouyangshengduo 4 * @description 只有当instance为null时,需要获取同步锁,创建一次实 5 * 例。当实例被创建,则无需试图加锁。线程安全,效率高 6 * 缺点:用双重if判断,复杂,容易出错 7 */ 8 public class Singleton { 9 private static Singleton instance=null; 10 private Singleton(){ 11 12 } 13 public static Singleton getInstance(){ 14 if(instance==null){ 15 synchronized(Singleton.class){ 16 if(instance==null){ 17 instance=new Singleton(); 18 } 19 } 20 } 21 return instance; 22 } 23 }
第四种:饿汉式,线程安全,但耗内存,因为只要加载这个类,就会实例化对象
package com.oysd.singleton; /** * @author ouyangshengduo * @description 初试化静态的instance创建一次。如果我们在Singleton类里 * 面写一个静态的方法不需要创建实例,它仍然会早早的创建一次实例。而降 * 低内存的使用率。 *缺点:没有lazy loading的效果,从而降低内存的使用率 */ public class Singleton { private static Singleton instance=new Singleton(); private Singleton(){ } public static Singleton getInstance(){ return instance; } }
第五种:静态内部类
1 package com.oysd.singleton; 2 /** 3 * @author ouyangshengduo 4 * @description 定义一个私有的内部类,在第一次用这个嵌套类时,会创 5 * 建一个实例。而类型为SingletonHolder的类,只有在 6 * Singleton.getInstance()中调用,由于私有的属性,他人无法使用 7 * SingleHolder,不调用Singleton.getInstance()就不会创建实例。 8 * 优点:达到了lazy loading的效果,即按需创建实例 9 */ 10 public class Singleton { 11 private Singleton(){ 12 13 } 14 private static class SingletonHolder{ 15 private final static Singleton instance=new Singleton(); 16 } 17 public static Singleton getInstance(){ 18 return SingletonHolder.instance; 19 } 20 }
然后通过单例模式来优化最开始出现的那个问题:
1 package com.oysd.singleton; 2 /** 3 * @author ouyangshengduo 4 * @description 单例模式运行效果 5 */ 6 7 //单例仓库类 8 class StoreHouse { 9 10 //仓库商品数量 11 private int quantity = 100; 12 //自己在内部实例化 13 private static StoreHouse ourInstance = new StoreHouse();; 14 //让外部通过调用getInstance()方法来返回唯一的实例。 15 public static StoreHouse getInstance() { 16 return ourInstance; 17 } 18 19 //封闭构造函数 20 private StoreHouse() { 21 } 22 23 public void setQuantity(int quantity) { 24 this.quantity = quantity; 25 } 26 27 public int getQuantity() { 28 return quantity; 29 } 30 } 31 32 33 //搬货工人类 34 class Carrier{ 35 public StoreHouse mStoreHouse; 36 public Carrier(StoreHouse storeHouse){ 37 mStoreHouse = storeHouse; 38 } 39 //搬货进仓库 40 public void MoveIn(int i){ 41 mStoreHouse.setQuantity(mStoreHouse.getQuantity()+i); 42 } 43 //搬货出仓库 44 public void MoveOut(int i){ 45 mStoreHouse.setQuantity(mStoreHouse.getQuantity()-i); 46 } 47 } 48 49 //工人搬运测试 50 public class SinglePattern { 51 public static void main(String[] args){ 52 StoreHouse mStoreHouse1 = StoreHouse.getInstance(); 53 StoreHouse mStoreHouse2 = StoreHouse.getInstance(); 54 Carrier Carrier1 = new Carrier(mStoreHouse1); 55 Carrier Carrier2 = new Carrier(mStoreHouse2); 56 57 System.out.println("两个是不是同一个?"); 58 59 if(mStoreHouse1.equals(mStoreHouse2)){ 60 System.out.println("是同一个"); 61 }else { 62 System.out.println("不是同一个"); 63 } 64 //搬运工搬完货物之后出来汇报仓库商品数量 65 Carrier1.MoveIn(30); 66 System.out.println("仓库商品余量:"+Carrier1.mStoreHouse.getQuantity()); 67 Carrier2.MoveOut(50); 68 System.out.println("仓库商品余量:"+Carrier2.mStoreHouse.getQuantity()); 69 } 70 }
运行结果:
1 两个是不是同一个? 2 是同一个 3 仓库商品余量:130 4 仓库商品余量:80