设计模式之单例模式

来源:互联网 发布:丝丝知我心微博 编辑:程序博客网 时间:2024/06/17 04:43

单例模式的定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例

单例模式的使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单利模式。

单例模式UML类图


* 构造函数不对外开放,一般为Private。
* 通过一个静态方法或者枚举返回单例类对象。
* 确保单例类的对象有且只有一个,尤其是在多线程环境下。
* 确保单例类对象在反序列化时不会重新构建对象。

示例

  • ###饿汉模式:是在声明静态对象时就已经初始化
public class Single {    private static final Single single=new Single();    private Single(){    }    public static Single getInstance(){        return single;    }}

在声明时就初始化对象,保证了对象的唯一性。
* ###懒汉模式:是声明一个静态对象,并且在用户第一次调用getInstance时进行初始化。

public class Single {    private static Single single;    private Single() {    }    public static synchronized Single getInstance() {        if (single == null) {            single = new Single();        }        return single;    }}

其中synchronized用于在多线程情况下保证单例对象唯一性。优点:在使用时才被实例化,节约了资源。缺点:第一次加载时需要实例化,反应慢,每次调用getInstance都进行同步,造成没必要的开销。一般不建议使用这种方式。
* ###Double Check Lock(DCL)实现单例

public class Single {    private static Single single = null;    private Single() {    }    public static Single getInstance() {        if (single == null) {            synchronized (Single.class) {                if (single == null) {                    single = new Single();                }            }        }        return single;    }}

优点:在需要时初始化单例,能够保证线程安全,对象初始化后调用getInstance不进行同步锁。缺点:第一次加载时反应较慢。是使用最多的单例实现方式。
* ###静态内部类单例模式

public class Single {    private Single() {    }    public static Single getInstance() {        return SingleHolder.single;    }    private static class SingleHolder {        private static final Single single = new Single();    }}

第一次调用getInstance方法会导致虚拟机加载SingleHolder类,能够保证线程安全、单例对象的唯一性,延迟了单例的实例化,是推荐使用的单例模式实现方式。

优点

  • 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁的创建和销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显。
  • 减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他的依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决。
  • 单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作。
  • 单例模式可以在系统设置全局的访问点,优化和共享资源访问。例如,可以设计一个单例类,负责所有数据表的映射处理。

缺点

  • 单例模式一般没有借口,扩展很困难,若要扩展,除了修改代码基本上没有第二种途径可以实现。
  • 单例对象如果持有Context,那么很容易引发内训泄露,此时需要注意传递给单例对象的Context最好是Application Context。
原创粉丝点击