面试题——单例模式与反射、序列化

饿汉式

立即加载,线程安全。

public class Singleton {
    private static Singleton INSTANCE = new Singleton();//立即加载
    private Singleton(){};
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

懒汉式

延迟加载,线程不安全。

public class Singleton {
    private static Singleton INSTANCE;//延迟加载
    private Singleton(){};
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new Singleton();
        } 
        return INSTANCE;
    }
}
双检锁(DCL)

延迟加载,线程安全。

public class Singleton {
    private volatile static Singleton INSTANCE;//延迟加载
    private Singleton(){};
    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton();
                }
            }
        }
        return INSTANCE;
    }
}

静态内部类

延迟加载,线程安全。
静态内部类属于被动引用,只有在调用时才加载。而JVM的类加载机制会保证只有一个线程成功执行方法,初始化类变量。

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

枚举

延迟加载,线程安全。

public class Singleton {
    private Singleton(){}
    
    //静态枚举类
    static enum SingletonHolder {
        INSTANCE;
        private Singleton singleton;

        private SingletonHolder() {
            singleton = new Singleton();
        }

        public Singleton getInstance() {
            return singleton;
        }
    }

    //对外暴露的获取方法
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE.getInstance();
    }
    
}

单例与反射

反射破坏单例:

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


    public static void main(String[] args) throws Exception {
        Class clazz = Singleton.class;
        Constructor constructor = clazz.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        Singleton o1 = (Singleton) constructor.newInstance();
        Singleton o2 = (Singleton) constructor.newInstance();
        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o1 == o2);//false
    }
}

一般而言有两种解决方案:

一:在私有构造中设置标志位,进行访问次数的判断,限制只能访问一次构造函数。

public class Singleton {
    private static boolean flag = false;
    private Singleton(){
        if (flag  == false) {
            flag = true;
        } else {
            throw new RuntimeException("反射尝试破坏...");
        }
    }
    private static class SingletonHolder {
        private static Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }


    public static void main(String[] args) throws Exception {
        Class clazz = Singleton.class;
        Constructor constructor = clazz.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        Singleton o1 = (Singleton) constructor.newInstance();
        Singleton o2 = (Singleton) constructor.newInstance();
        System.out.println(o1);
        System.out.println(o2);
        System.out.println(o1 == o2);
    }
}

面试题——单例模式与反射、序列化_第1张图片

二:直接使用枚举式单例,枚举没有无参构造,天然单例防反射。

单例与序列化

序列化单例对象,再反序列化也可以破坏单例模式:

public class Singleton implements Serializable{
    private static boolean flag = false;
    private Singleton(){
        if (flag  == false) {
            flag = true;
        } else {
            throw new RuntimeException("反射尝试破坏...");
        }
    }
    private static class SingletonHolder {
        private static Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }


    public static void main(String[] args) throws Exception {
        Singleton source = Singleton.getInstance();
        writeObject(source);//序列化
        Singleton target = (Singleton) readObjcet();//反序列化
        System.out.println(source);
        System.out.println(target);
        System.out.println(source == target);
    }

    public static void writeObject(Object o) {
        FileOutputStream fileOut = null;
        ObjectOutputStream out = null;
        try {
            fileOut = new FileOutputStream("test.txt");
            out = new ObjectOutputStream(fileOut);
            out.writeObject(o);
            System.out.println("Serialized data is saved");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileOut.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static Object readObjcet() {
        Object temp = null;
        FileInputStream fileIn = null;
        ObjectInputStream in = null;
        try {
            fileIn = new FileInputStream("test.txt");
            in = new ObjectInputStream(fileIn);
            temp  =  in.readObject();
            System.out.println("Deserialized Object...");
            return temp;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                fileIn.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

面试题——单例模式与反射、序列化_第2张图片

解决方案:添加readResolve()方法,在方法中返回单例

public class Singleton implements Serializable{
    private static boolean flag = false;
    private Singleton(){
        if (flag  == false) {
            flag = true;
        } else {
            throw new RuntimeException("反射尝试破坏...");
        }
    }
    private static class SingletonHolder {
        private static Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }

    protected Object readResolve() throws ObjectStreamException{
        System.out.println("调用了readReslove...");
        return SingletonHolder.INSTANCE;
    }
}

面试题——单例模式与反射、序列化_第3张图片

原理:在ObjectInputStream类的readOrdinaryObject()方法中,对readResolve()方法的存在做了判断,如果存在,则调用该方法获取返回值作为最终返回。

面试题——单例模式与反射、序列化_第4张图片

你可能感兴趣的:(面试题,单例,反射,序列化)