5分钟学会利用反射破坏单例模式解决方案

                                               反射破坏单例模式解决方案

一、前言

       如何利用反射破坏单例模式,以及如何防止反射机制破坏单例模式的解决方案。

              如有想了解单例模式的实现方式(饿汉式、懒汉式、登记式、ThreadLocal 线程单例),请前往以下链接:

                     5分钟学会java设计模式之单例模式

       在此记录一下,分享给大家。

二、反射破坏单例 - 代码演示

/**
 * 描述:懒汉式单例 - 静态内部类
 *     优点:线程安全,兼顾饿汉式的内存浪费,也兼顾synchronized锁的性能问题
 *     缺点:通过反射机制可以破坏单例
 *
 * @author yys
 */
public class LazyInnerClassSingleton {

    // 私有构造,防止外界new对象
    private LazyInnerClassSingleton() {}

    // 关键字作用:static 是为了使单例的空间共享 / final 保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance() {
        // 在结果返回以前,会先加载内部类
        return LazyHolder.LAZY;
    }

    // 默认不加载
    private static class LazyHolder {
        // 这里其实是饿汉式单例
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

}
/**
 * 描述:反射破坏单例测试
 * @author yys
 */
public class LazyInnerClassSingletonTest {

    public static void main(String[] args) {
        try {
            // 获取class对象
            Class<?> clazz = LazyInnerClassSingleton.class;

            // 通过反射拿到私有构造方法
            Constructor<?> c = clazz.getDeclaredConstructor(null);
            // private修饰:暴力反射
            c.setAccessible(true);

            // 调用构造,第一次
            Object o1 = c.newInstance();
            System.out.println(o1);

            // 调用构造,第二次
            Object o2 = c.newInstance();
            System.out.println(o2);

            System.out.println(o1 == o2);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
    /**
     * 描述:结果显示
     * @author yys
     */
    com.yys.demo.design.patterns.singleton.LazyInnerClassSingleton@7adf9f5f
    com.yys.demo.design.patterns.singleton.LazyInnerClassSingleton@85ede7b
    false

    // 总结:通过结果可以看出,通过反射机制(违背单例模式只有一个实例的原则)成功破坏单例模式。

三、防止反射破坏单例 - 解决方案

/**
 * 描述:懒汉式单例 - 静态内部类
 *     优点:线程安全,兼顾饿汉式的内存浪费,也兼顾synchronized锁的性能问题
 *     缺点:通过反射机制可以破坏单例
 *
 *     反射机制破坏单例解决方案:在构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常
 *
 * @author yys
 */
public class LazyInnerClassSingleton {

    // 私有构造,防止外界new对象
    private LazyInnerClassSingleton() {

        /**
         * 添加如下代码 - 防止反射机制破坏单例
         */

        if(null != LazyHolder.LAZY) {
            throw new RuntimeException("不允许通过反射机制创建多个实例");
        }

    }

    // 关键字作用:static 是为了使单例的空间共享 / final 保证这个方法不会被重写,重载
    public static final LazyInnerClassSingleton getInstance() {
        // 在结果返回以前,会先加载内部类
        return LazyHolder.LAZY;
    }

    // 默认不加载
    private static class LazyHolder {
        // 这里其实是饿汉式单例
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

}
    // 再次进行测试...

扫描二维码关注公众号,回复: 10813358 查看本文章
    // 总结:通过在构造方法中做一些限制,一旦出现多次重复创建,则直接抛出异常。

 四、枚举单例模式  - 得天独厚防止反射机制破坏单例

/**
 * 描述:枚举式单例
 * @author yys
 */
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;
    }

}

       1、无参构造方式

/**
 * 描述:反射破坏单例测试 - 无参构造测试
 * @author yys
 */
public class EnumSingletonTest {

    public static void main(String[] args) {

        try {

            /**
             * 测试 - (无参构造)反射是否破坏单例
             *      java.lang.NoSuchMethodException: com.yys.demo.design.patterns.singleton.EnumSingleton.<init>() at java.lang.Class.getConstructor0 :没有找到无参的构造方法
             */
            Class clazz = EnumSingleton.class;
            Constructor c = clazz.getDeclaredConstructor();
            c.newInstance();

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
    // 进行测试...

 

    // 总结:异常信息表明:枚举中并没有无参构造,故得天独厚的防止了无参构造反射获取实例。

    // 查看枚举类源码,发现只有一个有参构造方法。jdk.1.8源码如下:

    /**
     * Sole constructor.  Programmers cannot invoke this constructor.
     * It is for use by code emitted by the compiler in response to
     * enum type declarations.
     *
     * @param name - The name of this enum constant, which is the identifier
     *               used to declare it.
     * @param ordinal - The ordinal of this enumeration constant (its position
     *         in the enum declaration, where the initial constant is assigned
     *         an ordinal of zero).
     */
    protected Enum(String name, int ordinal) {
        this.name = name;
        this.ordinal = ordinal;
    }

        2、有参构造方式

/**
 * 描述:反射破坏单例测试 - 有参构造测试
 * @author yys
 */
public class EnumSingletonTest {

    public static void main(String[] args) {

        try {

            /**
             * 测试 - (有参构造)反射是否破坏单例
             *      java.lang.IllegalArgumentException: Cannot reflectively create enum objects : 不能用反射来创建枚举类型
             *
             *      在JDK1.8源码中,newInstance()方法中,416行, 判断如果是Modifier.ENUM枚举类型,直接抛出异常(源码强制性判断,为枚举式单例保驾护航)
             */
            Class clazz2 = EnumSingleton.class;
            Constructor c2 = clazz2.getDeclaredConstructor(String.class, int.class);
            c2.setAccessible(true);
            c2.newInstance("yys", 013);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
    // 再次进行测试...

 

    // 总结:异常信息表明:不能用反射来创建枚举类型

    // 本质原因说明:在JDK1.8源码中,newInstance()方法中,416行,判断如果是Modifier.ENUM枚举类型,直接抛出异常(源码强制性判断,为枚举式单例保驾护航)

    // 源码如下:

    @CallerSensitive
    public T newInstance(Object ... initargs)
        throws InstantiationException, IllegalAccessException,
               IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0)
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    }


    // 判断如果是Modifier.ENUM枚举类型,直接抛出异常
    // 源码强制性判断,为枚举式单例保驾护航
    if ((clazz.getModifiers() & Modifier.ENUM) != 0)
        throw new IllegalArgumentException("Cannot reflectively create enum objects");
        
    // 总结:枚举单例模式,天生就可以防止反射机制破坏单例。

                       Now ~ ~ ~写到这里,就写完了,如果有幸帮助到你,请记得关注我,共同一起见证我们的成长

发布了74 篇原创文章 · 获赞 253 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/qq_42175986/article/details/97386670