单例模式-下

Posted yuanjiehe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式-下相关的知识,希望对你有一定的参考价值。

破坏单例模式的方法

序列化破坏

代码演示

// 内部类实现的单例模式
public class Singleton implements Serializable {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
// 序列化
public class SingletonTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Singleton instance = Singleton.getInstance();
        // 序列化
        ObjectOutputStream oss = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oss.writeObject(instance);

        // 反序列化
        File file = new File("singleton_file");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
        Singleton newInstance = (Singleton)ois.readObject();

        // 比较
        System.out.println(instance);
        System.out.println(newInstance);
    }

运行结果

技术图片

由运行结果不难发现两个对象实例不相同,违反了单例模式的初衷

解决方案

代码演示

// 修改后的单例模式
public class Singleton implements Serializable {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }

    private Object readResolve() {
        return SingletonHolder.instance;
    }
}

运行结果

技术图片

解决方案分析

由于Singleton实现了Serializable接口,使其可序列化,若未加readResolve函数便会利用反射方法开辟内存空间创建新的实例实现深拷贝,当加上readResolve方法时将会调用该方法实现浅拷贝从而避免出现多个实例。

参考博客:https://blog.csdn.net/u014653197/article/details/78114041

反射攻击

代码演示

// 内部类实现的单例模式
public class Singleton implements Serializable {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static Singleton instance = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
public class SingletonTest {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        Singleton instance = Singleton.getInstance();

        Class objectClass = Singleton.class;
        // 获取构造器
        Constructor constructor = objectClass.getDeclaredConstructor();
        // 修改构造器权限
        constructor.setAccessible(true);
        // 利用构造器构造对象
        Singleton newInstance = (Singleton) constructor.newInstance();

        System.out.println(instance);
        System.out.println(newInstance);
    }
}

运行结果

技术图片

解决方法

修改Singleton类的私有构造器,使其试图通过反射方法构造时将会抛出异常

    private Singleton() {
        if (SingletonHolder.instance != null) {
            throw new RuntimeException("Singleton禁止反射调用");
        }
    }

另一种单例模式实现方法

实现代码

// 枚举类型实现的单例模式
public enum EnumSingleton {
    INSTANCE;
    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

优点

通过枚举类的特性使得JVM来帮我们保证线程安全和单例实例的问题

深入了解

通过Jad工具对EnumSingleton.class文件进行反编译得到如下代码

// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3) 
// Source File Name:   EnumSingleton.java

package com.reget.design.singleton;


public final class EnumSingleton extends Enum
{

    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }

    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(com/reget/design/sigleton/EnumSingleton, name);
    }

    private EnumSingleton(String s, int i)
    {
        super(s, i);
    }

    public Object getData()
    {
        return data;
    }

    public void setData(Object data)
    {
        this.data = data;
    }

    public static EnumSingleton getInstance()
    {
        return INSTANCE;
    }

    public static final EnumSingleton INSTANCE;
    private Object data;
    private static final EnumSingleton $VALUES[];

    static 
    {
        INSTANCE = new EnumSingleton("INSTANCE", 0);
        $VALUES = (new EnumSingleton[] {
            INSTANCE
        });
    }
}

以上是关于单例模式-下的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

单例模式下的通用宏

性能比较好的单例写法

Winform 单例模式下,能否传值?怎么传值?

C++11标准下的单例设计模式

单例模式-下