【设计模式】单例模式详解

内容由B站UP主产出,本文仅作为学习笔记

单例模式

定义:在程序运行过程中,只保持一个实例对象,就是使用了单例设计模式
优点:避免了创建对象、销毁对象的性能开销


懒汉式

public class LazySingleton{
	private static LazySingleton lazySingleton = null;

	private LazySingleton{}

	public static synchronized LazySingleton getInstance(){
		if(lazySingleton == null) {
			lazySingleton = new LazySingleton();
		}
		return lazySingleton;
	}
}

tips

  • private: 保证私有,外部无法new对象;
  • static: 必须是静态的,确保外部可直接调用getInstance()方法
  • synchronized:确保线程安全,if(lazySingleton == null)判断和lazySingleton = new LazySingleton();之间可能因其他线程插入而导致对象重新初始化,造成新的对象产生破坏单例。

双重检查

public class DoubleCheckSingleton{
	private static volatile DoubleCheckSingleton singleton = null;

	private DoubleCheckSingleton{}

	public static DoubleCheckSingleton getInstance(){
		if (singleton == null) {
			synchronized (DoubleCheckSingleton.class) {
				if (singleton == null) {
					singleton = new DoubleCheckSingleton();
				}
			}
		}
		return singleton;
	}
}

tips:

  • volatile: 需保证singleton在线程间的可见性

饿汉式

public class HungrySingleton{
	// 方式一
	// private static HungrySingleton singleton = new HungrySingleton();
	// 方式二
	private static HungrySingleton singleton = null;
	static {
		singleton = new HungrySingleton();
	}

	private HungrySingleton{}
	
	public static HungrySingleton getInstance(){
		return singleton;
	}
}

原理与静态内部类相同


静态内部类

public class SingletonClass{
	private static class InnerClass{
		private static SingletonClass singleton = new SingletonClass();
	}
	
	private SingletonClass{}

	public static SingletonClass getInstance(){
		return InnerClass.singleton;
	}
}

由于static修饰的类、方法、变量存在于方法区 / 元数据区中,所以其内部的SingletonClass singleton成员变量也只会初始化一次,因此实现单例模式。


枚举

public enum EnumSingleton{
	SINGLETON;

	public static EnumSingleton getInstance(){
		return SINGLETON;
	}
}

tips:

枚举是实现单例的最佳方式,可以有效防止对单例模式的破坏

  • 序列化和反序列化破坏单例模式
    • 原因:反序列化创建对象时,会判断是否重写readResolve方法,如果重写了,则返回自己的单例对象;如果没有重写,则通过反射创建一个新的对象,反射会破坏单例模式。
    • 解决办法:使用枚举 or 重写readResolve方法
    private Object readResolve() {
    	return singleton;
    }
    
  • 反射破坏单例模式
    • 反射:通过字节码文件创建构造器对象,通过构造器初始化单例对象
      • 注意,由于构造器是私有的,所以需要赋予权限constructor.setAccessible(true);
    • 解决办法:使用枚举 or 阻止通过构造器创建对象(在构造器中手动抛出异常),例如
    public class LazySingleton{
    	private static LazySingleton lazySingleton = null;
    
    	private LazySingleton{
    		if(lazySingleton == null) {
    			throw new RuntimeException("不允许通过反射创建单例对象");
    		}
    	}
    
    	public static synchronized LazySingleton getInstance(){
    		if(lazySingleton == null) {
    			lazySingleton = new LazySingleton();
    		}
    		return lazySingleton;
    	}
    }
    
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值