单例模式的定义
Ensure a class has only one instance, and provide a global point of access to it. 即确保某各类只有一个实例,并且提供一个全局的入口。
通用类图如下:
Singleton类称为单例类,通过使用私有构造方法确保在系统中只产生一个实例。按照实例初始化的时间,单例模式可以分成两类:饿汉式单例模式和懒汉式单例模式。顾名思义:恶汉式单例模式是在类加载时便完成单例实例的创建,而懒汉式单例模式是在使用时,才进行单例实例的创建。代码分别如下:
- 饿汉式
public class Singleton {
private static final Singleton singleton = new Singleton();
//私有构造方法
private Singleton(){
}
//获取实例对象方法
public static Singleton getSingleton(){
return singleton;
}
//其他方法,尽量是static
public static void doSomething(){
}
}
- 懒汉式
public class Singleton {
private static Singleton singleton = null;
//私有构造方法
private Singleton(){
}
//获取实例对象方法
public static Singleton getSingleton(){
if(singleton == null){
//考虑高并发情况
synchronized (Singleton.class) {
if(singleton == null){
singleton = new Singleton();
}
}
}
return singleton;
}
//其他方法,尽量是static
public static void doSomething(){
}
}
饿汉式单例模式和懒汉式单例模式均有各自的试用场景,只是懒汉式单例模式在第一次创建单例实例时,需考虑在高并发场景下可能会在内存中创建多个实例的情况。两种实现方式可以根据需要自行选择,推荐试用恶汉式单例模式。
单例模式的应用
- 单例模式的有点
- 由于单例模式在内存中只有一个实例,因此内存开支较小。
- 由于单例模式只需生成一次实例,因此会大大减少系统的性能开销。当一个对象需要频繁创建创建和销毁,且创建和销毁的性能无法优化时,单例模式的优势尤为明显。
- 单例模式可以避免对资源的多重占用。
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问。
- 单例模式的缺点
- 单例模式一般没有接口,很难扩展。
- 单例模式不利于测试。在并行开发环境中,如果单例模式没有完成,是不能进行测试的,没有接口也不能使用mock的方式虚拟一个对象。
- 单例模式与单一职责原则(SRP)有冲突。单一职责原则要求一个类应该只实现一个逻辑,而不关心它是否是单例,是不是要单例应取决于环境,而单例模式把“要单例”和业务逻辑融合在一个类中。
- 单例模式使用场景
- 要求生产唯一序列号的环境。
- 系统或项目中的共享资源。
- 创建一个资源系统开销较大。
- 需要定义搭理静态常量和静态方法的环境,可以采用单例模式。
- 单例模式的注意事项
- 线程安全。高并发情况下,懒汉式单例模式需要考虑线程安全问题,即有两个或以上线程同时去创建单例实例。
- 单例的复制。在Java中,对象默认是不可以复制的,若实现了Cloneable接口,并且实现了clone方法,则可以直接通过对象复制的方式创建一个新对象,此时对象复制是不需要调用构造方法的,因此即使是私有的构造方法,此时的对象依然可以被复制。因此单例类最好不要实现Cloneable接口。
最佳实践
单例模式是23中设计模式中比较简单的设计模式,应用也十分广泛。在Spring中,每个Bean默认都是单例的,这样做的优点是Spring的Bean容器可以管理这些Bean的生命周期。如果采用非单例模式(Prototype), 则Bean初始化之后交由J2EE容器管理,Spring容器不在跟踪管理这些Bean的生命周期。
《注》以上内容总结自秦小波-《设计模式之禅》,仅为个人学习笔记。
网友评论