欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-04-11 18:44:21
...

饿汉式

立即加载,线程安全。

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的类加载机制会保证只有一个线程成功执行<clinit>方法,初始化类变量。

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);
    }
}

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

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

单例与序列化

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

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();
            }
        }
    }
}

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

解决方案:添加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;
    }
}

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

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

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