设计模式笔记--单例模式

 

常用设计模式有23中,分为:

创建型模式(主要用于创建对象)

 

1、单例模式    2、工厂方法模式    3、抽象工厂模式    4、建造者模式     5、原型模式 
行为型模式 (主要用于描述对象或类是怎样交互和怎样分配职责)

 

1、模板方法模式  2、中介者模式  3、命令模式    4、责任链模式   5、策略模式   6、迭代器模式  

7、观察者模式      8、备忘录模式   9、访问者模式   10、状态模式  11、解释器模式

结构型模式(主要用于处理类或对象的组合)

1、代理模式  2、装饰模式   3、适配器模式   4、组合模式   5、外观模式(门面模式)   6、享元模式   7、桥梁模式

 

单例模式 

 
单例模式(Singleton Pattern)是一个比较简单的模式,其定义如下:
 
 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。    
 
设计模式笔记--单例模式_第1张图片
 
 
Singleton类称为单例类,通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton())。  
 
单例模式通用代码 : 
 
 
单例模式的使用场景
 
在一个系统中,要求一个类有且仅有一个对象,如果出现多个对象就会出现“不良反 应 ”,可以采用单例模式.
具体的场景如下:
 
● 要求生成唯一序列号的环境;
 
● 在整个项目中需要一个共享访问点或共享数据,例如一个Web页面上的计数器, 使用单例模式保持计数器的值,并确保是线程安全的;
 
● 创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源;
 
● 需要定义大量的静态常量和静态方法(如工具类)的环境,可以采用单例模式(当 然,也可以直接声明为static的方式)。  
 

注意事项  

 
首先,在高并发情况下,请注意单例模式的线程同步问题 
 
其次,需要考虑对象的复制情况 ( 单例类不要实现Cloneable接口  )
 

有上限的多例模式

需要产生固定数量对象的模式就叫做有上限的多例模式,它是单例模式的一种扩展.
 
采用有上限的多例模式,我们可以在设计时决定在内存中有多少个实例,方便系统进行扩展,修正单例可能存在的性能问题,提供系统的响应速度。例如读取文件,我们可以在系统启动时完成初始化工作,在内存中启动固定数量的reader实例,然后在需要读取文件时就可以快速响应。  
 

 

3.实现

3.4 饿汉式

//单例类.   
public class Singleton {
    
    private Singleton() {//构造方法为private,防止外部代码直接通过new来构造多个对象
    }

    private static final Singleton single = new Singleton();  //在类初始化时,已经自行实例化,所以是线程安全的。

    public static Singleton getInstance() {  //通过getInstance()方法获取实例对象
        return single;
    }
}  
优点:写法简单,线程安全。
  • 缺点:没有懒加载的效果,如果没有使用过的话会造成内存浪费。

3.1 懒汉式(线程不安全)

//单例类
public class Singleton {
    private Singleton() {
    }

    private static Singleton single = null;

    public static Singleton getInstance() {
        if (single == null) {
            single = new Singleton();  //在第一次调用getInstance()时才实例化,实现懒加载,所以叫懒汉式
        }
        return single;
    }
} 
  • 优点:实现了懒加载的效果。
  • 缺点:线程不安全。

3.1 懒汉式(线程安全)

//单例类
public class Singleton {
    private Singleton() {
    }

    private static Singleton single = null;

    public static synchronized Singleton getInstance() { //加上synchronized同步 
        if (single == null) {
            single = new Singleton();
        }
        return single;
    }
}  
  • 优点:实现了懒加载的效果,线程安全。
  • 缺点:使用synchronized会造成不必要的同步开销,而且大部分时候我们是用不到同步的。

3.3 双重检查锁定(DCL)

public class Singleton {
    private volatile static Singleton singleton; //volatile 能够防止代码的重排序,保证得到的对象是初始化过

    private Singleton() {
    }

    public static Singleton getSingleton() {
        if (singleton == null) {  //第一次检查,避免不必要的同步
            synchronized (Singleton.class) {  //同步
                if (singleton == null) {   //第二次检查,为null时才创建实例
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
} 
  • 优点:懒加载,线程安全,效率较高
  • 缺点:volatile影响一点性能,高并发下有一定的缺陷,某些情况下DCL会失效,虽然概率较小。

3.5 静态内部类

public class Singleton {
    private Singleton() {
    }

    public static Singleton getInstance() {
        //第一次调用getInstance方法时才加载SingletonHolder并初始化sInstance
        return SingletonHolder.sInstance;
    }
    
    //静态内部类
    private static class SingletonHolder {
        private static final Singleton sInstance = new Singleton();
    }
}
  • 优点:懒加载,线程安全,推荐使用

3.6 枚举单例

public enum Singleton {

    INSTANCE;   //定义一个枚举的元素,它就是Singleton的一个实例

    public void doSomething() {
    }
}  
优点:线程安全,写法简单,能防止反序列化重新创建新的对象。
  • 缺点:可读性不高,枚举会比静态常量多那么一丁点的内存。

3.7 使用容器实现单例模式

//单例管理类
public class SingletonManager {
    private static Map objMap = new HashMap();

    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);//添加单例
        }
    }

    public static Object getService(String key) {
        return objMap.get(key);//获取单例
    }
}
  • 优点:方便管理。
  • 缺点:写法稍复杂。

4.注意事项

  1. 使用反射能够破坏单例模式,所以应该慎用反射
    Constructor con = Singleton.class.getDeclaredConstructor();
    con.setAccessible(true);
    // 通过反射获取实例
    Singleton singeton1 = (Singleton) con.newInstance();
    Singleton singeton2 = (Singleton) con.newInstance();
    System.out.println(singeton1==singeton2);//结果为false,singeton1和singeton2将是两个不同的实例
  • 可以通过当第二次调用构造函数时抛出异常来防止反射破坏单例,以懒汉式为例:
public class Singleton {
    private static boolean flag = true;
    private static Singleton single = null;

    private Singleton() {
        if (flag) {
            flag = !flag;
        } else {
            throw new RuntimeException("单例模式被破坏!");
        }
    }

    public static Singleton getInstance() {
        if (single == null) {
            single = new Singleton();
        }
        return single;
    }
}  
  1. 反序列化时也会破坏单例模式,可以通过重写readResolve方法避免,以饿汉式为例:
public class Singleton implements Serializable {
    private Singleton() {
    }

    private static final Singleton single = new Singleton();

    public static Singleton getInstance() {
        return single;
    }

    private Object readResolve() throws ObjectStreamException {//重写readResolve()
        return single;//直接返回单例对象
    }
} 

5.应用场景

  • 频繁访问数据库或文件的对象。
  • 工具类对象;
  • 创建对象时耗时过多或耗费资源过多,但又经常用到的对象;

6.优点

  • 内存中只存在一个对象,节省了系统资源。
  • 避免对资源的多重占用,例如一个文件操作,由于只有一个实例存在内存中,避免对同一资源文件的同时操作。

7.缺点

  • 获取对象时不能用new
  • 单例对象如果持有Context,那么很容易引发内存泄露。
  • 单例模式一般没有接口,扩展很困难,若要扩展,只能修改代码来实现。

你可能感兴趣的:(Android之设计模式,单例,设计模式)