单例设计模式

单例模式

基本概念

单例模式,顾名思义是指只有一个实例的对象。

我们知道在java中对象是通过new出来的,每new一次就会产生一个新的对象,那么如何限制只产生一个对象呢?

每个对象都有构造函数,每当new对象时都会调用对象的构造函数来生成对象。 此时我们可以将构造函数设置成private来限制外部的访问,同时在类内部new一个并返回给外部使用。

单例模式参考代码如下:

public class Singleton {
	// 在内部先new一个出来
	public static final Singleton singleton = new Singleton();
	
	// 构造函数为私有函数,外部无法调用,也就保证了只会有一个实例。
	private Singleton() {
		// 构造方法
	}
}

写法

  • 饿汉式 (同基本概念中的代码)
public class Singleton {
	// 在内部先new一个出来
	public static final Singleton singleton = new Singleton();
	
	// 构造函数为私有函数,外部无法调用,也就保证了只会有一个实例。
	private Singleton() {
		// 构造方法
	}

	public static Singleton getInstance() {
		return singleton;
	}
}

优点:获取对象时较快(因为类加载时就已经new出对象了),同时避免了多线程的问题。

缺点:因为在类加载时就new出来了,可能会造成内存浪费(如果没用到该单例对象,也被实例化了)。

这个方式叫饿汉式,可能是形象的比喻该汉子比较饥饿,不管三七二十一先new一个出来填饱肚子吧。

  • 懒汉式
public class Singleton {
	
	public static Singleton singleton;
	
	private Singleton() {
		// 构造方法
	}

	public static Singleton getInstance() {
		// 延迟构造,在调用时才new对象
		if (singleton == null) {
			singleton = new Singleton();
		}

		return singleton;
	}
}

优点:在调用时才new对象,不会造成内存浪费。

缺点:构建对象的过程放在了调用的时候,获取对象时可能有一些慢。同时,该写法在多线程环境下,可能会存在多个实例,与设计不符。(如A线程执行到判断对象是否为空时,正在new对象,但是new对象需要一定的时间,此时B线程也执行到判断对象是否为空,但此时A线程还没有成功将对象创建出来,此时B就也会创新新的对象,从而存在多个实例)

针对此缺点,可以给getInstance方法加 synchronized 关键字来保证线程安全问题。

  • 双重检查式
public class Singleton {
	
	public static Singleton singleton;
	
	private Singleton() {
		// 构造方法
	}

	public static Singleton getInstance() {
		// 第一层检查,每个线程均可访问
		if (instance == null) {
			// 对Singleton类对象加锁,只有一个线程可以访问。 
      synchronized (Singleton.class) {
          if (instance == null) {
              instance = new Singleton();
          }
      }
    }

		return singleton;
	}
}

优点:只有第一次获取对象时才会进行线程同步。(相比在方法上加synchronized关键字,这种代码块的同步开销更小)

缺点:第一次加载时可能较慢;高并发环境时这种双重校验也会失效 ,参见博客文章解释。

  • 静态内部类方式
public class Singleton {
    private Singleton() {}
		
		// 单例持有类
    private static class SingletonHodler {
        private static final Singleton instance = new Singleton();
    }
    public static Singleton getInstance(){
        return SingletonHodler.instance;
    }
}

优点:该方式解决了懒汉式线程不安全问题,同时也保持了延迟加载的优点。

缺点:使用时才会加载可能在调用时有些慢。

一般在日常开发过程中,较为推荐使用此种方式来创建单例 。

扩展

在《设计模式之禅》一书中,提到了有时可能需要创建一定数量的实例。那么可以在单例模式的基础之上进行改进,如在单例类中加入集合或map来存储单例对象。 伪代码如下:

public class Singleton {
	private int maxCount = 3;
	private List<Singleton> instanceList = new ArrayList();	

	static {
        for (int i = 0; i < maxCount ; i++) {
            instanceList.add(new Singleton())
        }
  }

	private Singleton() {

	}
	
	private Singleton getInstance() {
		// 根据业务逻辑返回需要的对象,此处用随机代替。 
		int random = (int) Math.random() * maxCount;
		instanceList.get(random);
	}
	
}

这种方式的使用场景如数据库的连接(经常在配置文件中确定最大的连接数量)。个人在开发中很少用到这种创建一定数量对象的写法。

总结

模式优点缺点
单例模式1、只有一个实例,减少了内存开支,节约资源。1、单例类一般没有接口,扩展困难,并且在单例类中容易混杂多个业务逻辑,与类的单一职责原则有冲突。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值