码迷,mamicode.com
首页 > 其他好文 > 详细

单例模式-下

时间:2020-04-03 00:19:35      阅读:62      评论:0      收藏:0      [点我收藏+]

标签:source   this   cat   alac   ref   run   jvm   权限   ack   

破坏单例模式的方法

序列化破坏

代码演示

// 内部类实现的单例模式
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
        });
    }
}

单例模式-下

标签:source   this   cat   alac   ref   run   jvm   权限   ack   

原文地址:https://www.cnblogs.com/YuanJieHe/p/12623976.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!