"单"身贵族爱好者必备:新手也能看懂的单例模式入门指南

发布时间 2023-11-09 09:43:44作者: 程序视点

大家好,欢迎来到程序视点!

前言

在前面三期里,我们详细讲解了工厂模式下的三种分类:

简单工厂模式(Simple Factory)

工厂方法模式(Factory Method)

抽象工厂模式(Abstract Factory)

其中,简单工厂模式不是我们23中设计模式之气,其他两个设计模式都属于创建型模式的设计方式。

今天,我们要接着讲另一种创建型模式:单例模式

单例模式

单例模式简介

单例模式是比较常见的一种设计模式,目的是保证一个类只能有一个实例。

这个实例就是其唯一的对象,可以直接访问,不需要在其他地方实例化该类的对象。

注意三点:

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

关键代码有两点:

  • 私有构造函数
  • 对外暴露获取唯一实例的方法

示例

关于单例模式的创建,我们不得不了解其中的两种方式:懒汉式和饿汉式。

首先来看懒汉式。之所以取这个名称,是因为类的实例并不是一开始就创建好的,而是“懒”到第一次获取的时候才创建。

public class Singleton {
	//构造函数私有化
	private Singleton(){}
	
  //“懒”就懒在这里。可以对比后面的饿汉式。
	private static Singleton singleton = null;
	
	//暴露获取唯一实例的方法
	public static Singleton getSingleton(){
		if(singleton == null){
			singleton = new Singleton();
		}
		return singleton;
	}
}

这种方式是最基本的实现方式。但这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized,所以严格意义上它并不算单例模式。

由于线程安全并不是我们要讲的主要内容,这里就不展开分析了

ps:在我们后面讲多线的时候,会详细分析。敬请期待!

再来看看饿汉式。这和上面的懒汉式不同:它是直接一开始就创建好一个实例化的对象。不会管这个实例会不会立即被使用。

class Singleton2 {
  //私有构造函数
	private Singleton2(){};
  // 太“饿”了,得先new个实例先垫下
	private static Singleton2 singleton2= new Singleton2();
  //暴露获取唯一实例的方法
	public static Singleton2 getSingleton2(){
		return singleton2;
	}
}

这种方式比较常用。同时不用去考虑加锁的问题,执行效率会高些!但有个缺点:类加载时就初始化,浪费内存。

?,我们已经实现了单例模式的创建了。

现在我们需要思考一个问题:懒汉式和饿汉式都有各自的缺点,那有没有一种单例模式能弥补这些缺点呢?

答案是:有的!我们常说的Double-Locked-Checking就是其中之一.

class Singleton3{
	//私有构造函数
	private Singleton3(){}
	
	private static Singleton3 singleton3 = null;
	
	public static Singleton3 getSingleton3(){
		//先检查实例是否存在,如果不存在才进入下面的同步块
		if(singleton3 == null){
			//同步块,线程安全的创建实例
			synchronized (Singleton3.class) {
				//再次检查实例是否存在,如果不存在才真正的创建实例
				if(singleton3 == null){
					singleton3 = new Singleton3();
				}
			}
		}
		return singleton3;
	}
}

这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

但这种方式还存在一个问题。我在这里先卖个关子。细心的小伙伴如果发现了,可以给我留言哦!ps:这里可能出现的问题我们在后期中会详细分析

DCL方式主要是在实例域需要延时初始化使用。我们还有一种在静态域使用的方式。

class Singleton4{
	private Singleton4(){};
	
	private static class Singleton4Holder{
		//静态初始化器,由JVM来保证线程安全
		private static Singleton4 singleton4 = new Singleton4();
	}
	
	public static Singleton4 getSingleton4(){
		return Singleton4Holder.singleton4;
	}
}

采用静态(类级)内部类的方式,将实例放在静态成员内部类中。这样内部类的实例与外部类的实例没有绑定关系,而且只有被调用到时才会装载,从而实现了延迟加载。

好了!我们今天的单例模式就介绍到这里。想要了解更多设计模式的信息,敬请关注微信公众号:程序视点。我们下期再会!