单例模式
常见的几种写法
一些扩展
单例模式
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例。
- 单例类必须给所有其他对象提供这一实例。
常见的几种写法
-
懒汉式(线程不安全)
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/