思维导图

概念

保障一个零碎中的一个类,只有一个实例

实现

私有参数

/** * 私有参数实现 */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 Singleton 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,所以是同一个对象