java设计模式之单例模式

限于喜欢 提交于 2019-11-28 18:05:39
单例模式
常见的几种写法
一些扩展

单例模式

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

常见的几种写法

  • 懒汉式(线程不安全)

    12345678910111213
    public class  {    private static Singleton instance;    private Singleton() {    }    public static Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}
  • 懒汉式(线程安全)

    12345678910111213
    public class Singleton {    private static Singleton instance;    private Singleton() {    }    public static synchronized Singleton getInstance() {        if (instance == null) {            instance = new Singleton();        }        return instance;    }}

    懒加载,但效率低

  • 饿汉式

    12345678910
    public class Singleton {    private static Singleton instance = new Singleton();    private Singleton() {    }    public static Singleton getInstance() {        return instance;    }}

    不具备懒加载

  • 静态内部类

    123456789101112
    public class Singleton {    private Singleton() {    }    public static final Singleton getInstance() {        return SingletonHolder.INSTANCE;    }    private static class SingletonHolder {        private static final Singleton INSTANCE = new Singleton();    }}

    懒加载

  • 枚举

    123456
    public enum Singleton {    INSTANCE;    public void whateverMethod() {    }}

    推荐
    能避免多线程同步问题
    能防止反序列化重新创建新的对象

  • 双重校验锁

    1234567891011121314151617
    public class Singleton {    private volatile static Singleton singleton;    private Singleton() {    }    public static Singleton getSingleton() {        if (singleton == null) {            synchronized (Singleton.class) {                if (singleton == null) {                    singleton = new Singleton();                }            }        }        return singleton;    }}

一些扩展

如何破坏单例
  • 反射
    虽然我们私有了构造方法,但是反射仍然能够通过AccessibleObject.setAccessible访问到私有方法,如:

    1234
    Class clazz = Singleton.class;Constructor cons = clazz.getDeclaredConstructor(null); cons.setAccessible(true);Singleton singleton = (Singleton) cons.newInstance(null);
  • 反序列化
    反序列化通过ObjectInputputStream来实现,下面来看代码

    123456789101112
    public final Object readObject()      throws IOException, ClassNotFoundException  {	...	...	int outerHandle = passHandle;	try {		Object obj = readObject0(false);		handles.markDependency(outerHandle, passHandle);	...	...  }

    进入readObject0方法

    12345678910111213141516
    private Object readObject0(boolean unshared) throws IOException {	...	...	try {		switch (tc) {			case TC_NULL:			return readNull();		case TC_REFERENCE:			return readHandle(unshared);		...		...		case TC_OBJECT:			return checkResolve(readOrdinaryObject(unshared));		case TC_EXCEPTION:		...}

    进入readOrdinaryObject方法

    12345678910111213
    private Object readOrdinaryObject(boolean unshared)        throws IOException{	...	Object obj;  	try {    		obj = desc.isInstantiable() ? desc.newInstance() : null;    	} catch (Exception ex) {            throw (IOException) new InvalidClassException(		desc.forClass().getName(),"unable to create instance").initCause(ex);    	}	...}
防止单例被破坏
  • 使用单元素枚举
  • 单例类中定义readResolve方法

参考

深入理解单例模式(上)
深入理解单例模式(下)

原文引用 大专栏  https://www.dazhuanlan.com/2019/08/27/5d64b6d0bd223/


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!