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
posted @ 2017-03-31 16:05  欧阳生朵  阅读(360)  评论(0编辑  收藏  举报