单例模式-终章

序言

两年之前其实我们讲过单例模式,但是当时是有疏忽的,所以填一下两年之前的坑,设计模式之单例设计模式。这次我们全面的去构造一个安全的,高效率的单例。

危险且无用的行为

你一定写过这样的代码,其实我也写过。

  public static  SingleModel getInstance(){
        if(instance == null){
            // 同步锁
            synchronized (SingleModel.class){
                //这里需2次判空
                if(instance==null){
                    instance = new SingleModel();
                }
            }
            instance = new SingleModel();
        }
        return instance;
    }

这段代码是我以前写的代码,这段代码乍一看,觉得没问题,线程安全的,且不说别的地方,我可以告诉你,这段代码并不是线程安全的,随着我不断在工作中吸取经验,我发现,这段代码应该是最无用而且危险的代码,但是很多人还在使用它,甚至引以为豪,认为自己的代码足够安全。这段代码为什么不安全,其实之后我意识到了这个问题并且出了一篇博客批评自己。为什么不安全?
而且
他也不是单例安全的,因为不能防止反编译和反射。尽管你可以把构造方法设置为private。

我来演示一个案例来证明一下:

public class SeriableSingleton implements Serializable {
	public final static SeriableSingleton INSTANCE = new SeriableSingleton();
	
	private SeriableSingleton() {
	}
	
	public static SeriableSingleton getInstance() {
		return INSTANCE;
	}
}

		SeriableSingleton instance = SeriableSingleton.getInstance();
		SeriableSingleton instance1 = SeriableSingleton.getInstance();
		Class<SeriableSingleton> seriableSingletonClass = SeriableSingleton.class;
		SeriableSingleton singleton = seriableSingletonClass.newInstance();
		System.out.println(instance == instance1);
		System.out.println(singleton == instance);

不妨執行一下看看結果,单例被破坏。所以这个并不好,也很笨,其实创建一个对象也是比较费时费力的事情,上去就创一个并不是一个明智的选择,所以有了所谓的懒汉式创建,可是懒汉式双锁是一个愚蠢的选择, 那么我们应该如何去正确的创建一个单例对象呢?

怎么做

为了保证单例模式不被破坏,必须声明所有的实例字段为transient,并且并提供一个readResolve方法,否则,每当序列化的实例被反序列化时都会被创建一个新的对象。那应该怎么办。

public class SafeSingleton implements Serializable {
	
	private SafeSingleton() {
		if (SafeSingletonHolder.instance != null) {
			throw new RuntimeException("不允许创建多个实例");
		}
	}
	
	private static class SafeSingletonHolder {
		private static SafeSingleton instance = new SafeSingleton();
	}
	
	public static SafeSingleton getInstance() {
		return SafeSingletonHolder.instance;
	}
	
	private Object readResolve(){ return SafeSingletonHolder.instance; }
}

首先这是一个懒加载的,其次这是一个无法被反射攻击的,同样的,序列化也无法攻击。所以说创建单例最好的是使用内部类创建!

结束语

之前我们写的代码是有问题的,在任何阶段,谁也无法保证写出没有任何问题的代码,但是我们要不断的去升级,去反省自己之前的工作,是否有不足,是否可以做的更好,代码是否有最优的解决方案,当前代码是否犯了同样的问题,这样,我们才能够不断的提升,不断的进步。为实现梦想添砖加瓦。

你可能感兴趣的:(设计模式,单例模式,java,开发语言)