设计模式之单例模式

设计模式分类

23种设计模式一共分为三种类型:

  • 创建型模式:单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式
  • 结构型模式:代理模式、装饰模式、适配器模式、组合模式、门面模式、享元模式、桥梁模式
  • 行为型模式:模版方法模式、中介者模式、命令模式、责任链模式、策略模式、迭代器模式、观察者模式、备忘录模式、访问者模式、状态模式、解释器模式

这一篇首先来介绍单例模式。

单例模式的概念

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式是23种设计模式中最简单的一种,单例模式保证了在系统运行的全局只有一个该类的实例对象,在特定的需求下有非常重要的作用。

举个栗子

一个简单的单例模型图

单例模式的常见实现方式有两种

  • 懒汉式
1
2
3
4
5
6
7
8
9
10
11
public class Singleton{
private static Singleton singleton=null;
public static synchronized SingletonClass getInstance(){
if(instance==null){
singleton =new SingletonClass();
}
return singleton;
}
private Singleton(){
}
}
  • 饿汉式
1
2
3
4
5
6
7
8
public class Singleton{
private static final Singleton singleton = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return singleton;
}
}

这两种实现方式保证了系统在运行时全局只存在唯一一个Singleton类的实例,区别在于懒汉式在需要的时候创建实例,可以节约系统开销,但是与此同时产生了多线程下可能产生多个实例的问题,需要加锁同步。结合两方面的问题,还有一种双重锁的单例实现模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Singleton{
private static volatile Singleton singleton=null;
private Singleton(){
}
public static Singleton getInstance(){
if(singleton ==null){
synchronized(Singleton.class){
if(singleton ==null){
singleton =new Singleton();
}
}
}
return singleton;
}
}

这种方式把同步放在if判断内部,不需要每次都同步,因此提升了懒汉式单例的性能。

适用范围

  1. 需要生成全局唯一变量的环境
  2. 需要进行大规模资源共享的环境
  3. 使用静态资源,方法的工具类

优点缺点

单例模式的优点在于全局只生成一个实例,减少了系统的开销,可以避免重复占用资源,方便进行全局的管理。

单例模式的缺点在于一旦被声明为单例,它的扩展性就会受到影响,所以单例模式只能在需要的时候使用。

注意事项

单例模式需要注意的最主要的问题就是多线程下面的同步问题,还有就是记住,只有需要使用单例的特定环境才能使用单例设计模式。

补充内容

上面我说的内容其实只是最基本的单例设计模式,实际应用中还有很多复杂的情况,也衍生了很多新的单例模式,所以在这里增加了一些补充的内容。

首先上面的单例即使同步了难道就真的做到单例了吗,其实不然,创建对象的可以不用new,创建对象有很多种方式:

  • 通过new关键词 【也只最常用的一种实现方式】
  • 通过反射机制实现 【这里通过反射有两种实现方式:a、Class类的newInstance方法创建对象 b、java.lang.relect.Constructor类里也有一个newInstance方法可以创建对象】
  • 通过clone方法实现 【该方法需要让当前类实现Cloneable接口 重写clone方法】
  • 通过序列化以及反序列化实现 【该方法需要当前类实现】

而一旦不使用new,上面的方式就不一定生效了,克隆的问题后面原型模式会详细分析,这里只看反序列化和反射,不幸的是,上面的方式都保证不了单例,于是有了一种新的方式

  • 枚举式
1
2
3
public enum Singleton{
INSTANCE;
}

在这种单例模式中,INSTANCE可以被看做是Singleton的一个实例,我们也可以定义它的属性和方法,用Singleton.INSTANCE可以获取到这个唯一的实例,这种方式避免了反射和反序列化的漏洞。调用的效率比较高,线程安全,实现简单。唯一的缺点是没有实现延时加载。

此外还有一种比较好的结合懒汉式和饿汉式的一种方式,利用静态内部类实现单例

1
2
3
4
5
6
7
8
9
10
public class Singleton{
private static class SingletonHolder{
private static singletonInstance=new Singleton();
}
private Singleton{
}
public static getSingleton{
return SingletonHolder. singletonInstance;
}
}

还有一种登记式的单例模式,由于不常见,在这里不说明了,单例模式到此全部结束。

-------------本文结束感谢您的阅读-------------
坚持原创技术分享,您的支持将鼓励我继续创作!