思维导图
概念
保障一个零碎中的一个类,只有一个实例
实现
私有参数
/** * 私有参数实现 */ public class Singleton { public static final Singleton INSTANCE = new Singleton(); private Singleton() { } }
长处:简略
毛病:违反了java的封装
饿汉式
/** * 饿汉式 */ public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton() { } public static Singleton getInstance(){ return INSTANCE; } }
长处:应用前就创立好实例,多线程平安
毛病:加载Singleton.class文件的时候,实例对象就创立了,节约了内存
懒汉式
/** * 懒汉式 */ public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; } }
毛病:多线程并发下不平安,可能会创立多个实例
长处:只有调用getInstance()办法才会创立实例
Double CheckLock
/** * Double CheckLock */ public class Singleton { private static Singleton instance; private Singleton() { } public static Singleton getInstance(){ if(instance == null){ //为什么要先判断在加锁 因为instance只有刚开始的时候为null, //前面都不会为null,防止后续应用中的不必要的同步 synchronized (Singleton.class){ if(instance == null){ instance = new Singleton(); } } } return instance; } }
长处:线程平安,效率高
毛病:实现简单
动态外部类
/** * 动态外部类 */ public class Singleton { private Singleton() { } public static Sing<p style="color:transparent">来源gao!%daima.com搞$代*!码网</p>leton getInstance(){ return SingletonHold.instance; } public static class SingletonHold{ private static Singleton instance = new Singleton(); } }
长处:线程平安,只有在调用getInstance办法时,才会创立实例
枚举
/** * 枚举单例 */ public enum Singleton { INSTANCE; }
长处:实现超级简略
序列化问题
测试代码
public class Singleton implements Serializable { private Singleton() { } public static Singleton getInstance() { return SingletonHold.instance; } static class SingletonHold { private static Singleton instance = new Singleton(); } public static void main(String[] args) throws Exception { try ( FileOutputStream fileOutputStream = new FileOutputStream("d://text.txt"); FileInputStream fileInputStream = new FileInputStream("d://text.txt"); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream) ) { Singleton s1 = Singleton.getInstance(); objectOutputStream.writeObject(s1); Singleton s2 = (Singleton) objectInputStream.readObject(); System.out.println(s1 == s2); } catch (Exception e) { System.out.println("序列化异样"); } } }
测试后果
剖析
序列化后的单例再把他序列化回来,是两个不同的对象
防止序列化问题
private Object readResolve() throws ObjectStreamException { return SingletonHold.instance; }
在之前的代码中退出这一个办法即可
为什么加了这一段代码就能够呢
343行:ObjectStreamClass的lookup办法
391行:lookup办法中的这一行结构了entry对象,进入构造方法
520行:反射查找要序列化的对象是否有writeObject办法
523行:反射查找要序列化的对象是否有readObject办法
526行:反射查找要序列化的对象是否有readObjectNoData办法
531行:反射查找要序列化的对象是否有writeReplace办法
533行:反射查找要序列化的对象是否有readResolve办法
2078行:反序列化的时候,如果有readResolve办法,就调用readResolve办法返回序列化对象
所以加了这一段代码之后,方序列化就会返回SingletonHold.instance,所以是同一个对象