Java——单例设计模式

单例设计模式

所谓单例模式,顾名思义,在使用单例设计时,单例对象的类必须保证只用一个实例存在

单例模式思路

  • 一个类能返回对象的一个引用和一个获得该实例的方法(该方法一定是静态方法,且通常为getInstance()方法)。
  • 当调用这个方法的时候,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
  • 同时将该类的构造方法定义为私有方法(必须为私有方法),这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。

单例设计模式特点

  • 单例类只能有一个实例
  • 单例类必须自己创建自己唯一的实例
  • 单例类必须给所以对象提供这一实例

单例设计模式的优点

  • 提供对唯一实例的受控访问:在单例模式中,活动的单例只有唯一一个实例,对单例类的所有实例化得到的都是相同的一个实例。这样就防止其它对象对自己的实例化,确保所有的对象都访问一个实例。
  • 提高系统的性能:由于在系统内存中只存在一个对象,因此可以节约系统资源,当需要频繁创建和销毁对象时单例模式可以提高系统的性能。
  • 允许可变数目的实例。
  • 单例模式具有一定的伸缩性,类自己来控制实例化进程,类就在改变实例化进程上有相应的伸缩性。
  • 避免对共享资源的多重占用。

单例设计模式的缺点

  • 由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
  • 不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。

单例设计模式的应用场景

单例设计模式主要用于封装常用工具类,保证整个应用常用的数据统一;保存一些共享数据在内存中,以供其他类可以随时读取

  • Windows操作系统的任务管理器
  • 回收站,在整个系统运行过程中,回收站一直维护着仅有的一个实例
  • 网站计数器,实现同步机制
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
  • 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
  • HttpApplication也是单位例的典型应用。所有的HttpModule都共享一个HttpApplication实例。

单例设计模式的两种形式——懒汉式,饿汉式

懒汉式单例
public class Singleton {
    private static Singleton instance;
    private Singleton() { 
    }
    public static synchronized Singleton getInstance() {
        if (instance == null) { 
            instance = new Singleton();
        }
        return instance;
    }
}

懒汉式单例的优点

  • 资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。

懒汉式单例的缺点

  • 懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同时创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。
饿汉式单例(推荐使用)
public class Singleton {
    private final static Singleton INSTANCE = new Singleton();
    private Singleton() {//单例的特点,构造必须私有
    }
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

饿汉式单例优点:

  • 线程安全
  • 在类加载的时候就已经创建好了一个静态对象,调用时反应速度快

饿汉式单例缺点:

  • 资源利用率不高,可能getInstance()方法永远不会执行到,但执行该类的其他静态方法或者加载了该类(class.forName),那么这个实例仍然初始化
懒汉式与饿汉式单例的区别
  • 懒汉式单例在调用getInstance()的时候才会取初始化这个单例;而饿汉式单例是在类加载的时候就完成单例,在调用getInstance()的时候,单例已经存在了
  • 线程方面:懒汉式单例本身非线程安全,要实现线程安全要加锁;饿汉式单例线程安全,可直接用于多线程的情况
  • 资源加载和性能方面:懒汉式单例会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后同饿汉式;饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成。
    一些其他的单例模式见博客:https://blog.csdn.net/jason0539/article/details/23297037

注意事项

  • 使用单例模式的时候不能用反射的模式来创建单例,否则会实例化一个新的对象
  • 使用懒汉式单例模式时要注意线程安全的问题
  • 饿汉式单例和懒汉式单例的构造方法都是私有的,所以不能被继承

猜你喜欢

转载自blog.csdn.net/L_X_Y_HH/article/details/81776396