设计模式-单例模式

反射花式破坏单例

懒汉式

  • 反射破坏单例代码演示:
package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author liouwb
 * 单例模式
 * 懒汉式
 */
public class SingletonLazyTest {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        // 懒汉式
        SingletonLazy lazy = SingletonLazy.getInstance();
        SingletonLazy lazy1 = SingletonLazy.getInstance();

        // 通过反射实例
        Class<SingletonLazy> lazyClass = SingletonLazy.class;
        Constructor<SingletonLazy> constructor = lazyClass.getDeclaredConstructor();

        constructor.setAccessible(true);
        SingletonLazy lazy2 = constructor.newInstance();

        // 验证单例是否全局只有一个实例
        System.out.println("正常获取的实例比较:" + (lazy == lazy1));
        System.out.println("正常获取实例与反射获取比较:" + (lazy == lazy2));

        // 输出对象信息
        System.out.println("lazy :" + lazy);
        System.out.println("lazy1:" + lazy1);
        System.out.println("lazy2:" + lazy2);

    }

    /**
     * 懒汉式
     */
    static class SingletonLazy {
        //
        public static SingletonLazy instance;

        // 私有构造函数
        private SingletonLazy() {
        }

        public static SingletonLazy getInstance() {
            if (instance == null) {
                instance = new SingletonLazy();
            }

            return instance;
        }
    }
}
  • 反射破坏单例结果输出:
正常获取的实例比较:true
正常获取实例与反射获取比较:false
lazy :singleton.SingletonLazyTest$SingletonLazy@4554617c
lazy1:singleton.SingletonLazyTest$SingletonLazy@4554617c
lazy2:singleton.SingletonLazyTest$SingletonLazy@74a14482

Process finished with exit code 0
结论:
  • 从结果可以看出,lazy 和 lazy1是正常获取的,是同一实例
  • lazy2通过反射获取的,与lazy 和 lazy1已经不是同一实例
  • 懒汉式的单例被反射破坏

饿汉式

  • 代码演示:
package singleton;


import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author liouwb
 * 单例模式
 * 饿汉式
 */
public class SingletonHungryTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 饿汉式
        SingletonHungry hungry = SingletonHungry.getInstance();
        SingletonHungry hungry1 = SingletonHungry.getInstance();

        // 通过反射实例
        Class<SingletonHungry> hungryClass = SingletonHungry.class;
        Constructor<SingletonHungry> constructor = hungryClass.getDeclaredConstructor();

        constructor.setAccessible(true);
        SingletonHungry hungry2 = constructor.newInstance();


        // 验证单例是否全局只有一个实例
        System.out.println("饿汉式正常获取的实例比较:" + (hungry == hungry1));
        // 正常获取实例与反射获取实例对比
        System.out.println("饿汉式正常获取的实例与反射获取实例比较:" + (hungry == hungry2));

        // 输出对象信息
        System.out.println("hungry :" + hungry);
        System.out.println("hungry1:" + hungry1);
        System.out.println("hungry2:" + hungry2);
    }

    /**
     * 饿汉式
     */
    static class SingletonHungry {
        private static SingletonHungry instance = new SingletonHungry();

        /**
         * 构造函数
         *
         * @return
         */
        private SingletonHungry() {
        }

        /**
         * @return
         */
        public static SingletonHungry getInstance() {

            return instance;
        }
    }
}

  • 输出结果:
饿汉式正常获取的实例比较:true
饿汉式正常获取的实例与反射获取实例比较:false
hungry :singleton.SingletonHungryTest$SingletonHungry@4554617c
hungry1:singleton.SingletonHungryTest$SingletonHungry@4554617c
hungry2:singleton.SingletonHungryTest$SingletonHungry@74a14482

Process finished with exit code 0
结论:
  • 从结果可以看出,hungry 和 hungry1 是正常获取的,是同一实例
  • hungry2通过反射获取的,与hungry 和 hungry1 已经不是同一实例
  • 饿汉式的单例被反射破坏

检查锁单例模式

  • 代码演示:
package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author liouwb
 * 单例模式
 * 懒汉式 加锁(饿汉式加锁,效果一样): 线程安全,可以被反射破坏
 */
public class SingletonLazyLockTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 懒汉式 加锁
        SingletonLazyLock lazyLock = SingletonLazyLock.getInstance();
        SingletonLazyLock lazyLock1 = SingletonLazyLock.getInstance();

        // 通过反射实例
        Class<SingletonLazyLock> lazyClass = SingletonLazyLock.class;
        Constructor<SingletonLazyLock> constructor = lazyClass.getDeclaredConstructor();

        constructor.setAccessible(true);
        SingletonLazyLock lazyLock2 = constructor.newInstance();

        // 验证单例是否全局只有一个实例
        System.out.println("正常获取的实例比较:" + (lazyLock == lazyLock1));
        // 反射获取与正常获取对比
        System.out.println("正常获取的实例比较:" + (lazyLock == lazyLock2));

        // 输出相应对象
        System.out.println("lazyLock :" + lazyLock);
        System.out.println("lazyLock1:" + lazyLock1);
        System.out.println("lazyLock2:" + lazyLock2);

    }

    /**
     * 懒汉式 加锁
     */
    static class SingletonLazyLock {
        //
        public static SingletonLazyLock instance;

        // 私有构造函数
        private SingletonLazyLock() {
        }

        public static synchronized SingletonLazyLock getInstance() {
            if (instance == null) {
                instance = new SingletonLazyLock();
            }else {
                throw new RuntimeException("禁止使用反射破坏单例");
            }


            return instance;
        }
    }
}

  • 结果输出:
正常获取的实例比较:true
正常获取的实例比较:false
lazyLock :singleton.SingletonLazyLockTest$SingletonLazyLock@4554617c
lazyLock1:singleton.SingletonLazyLockTest$SingletonLazyLock@4554617c
lazyLock2:singleton.SingletonLazyLockTest$SingletonLazyLock@74a14482

Process finished with exit code 0
结论:
  • 从结果可以看出,lazyLock和 lazyLock1是正常获取的,是同一实例
  • lazyLock2通过反射获取的,与lazyLock和 lazyLock1已经不是同一实例
  • 加锁之后的单例看似很安全,但仍然可以被反射破坏

双重检查锁

  • 代码演示:
package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author liouwb
 * 单例模式 双重检查锁
 */
public class SingletonDoubleCheckLockTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 懒汉式 双重检查锁
        SingletonLazyDoubleLock lazyDoubleLock = SingletonLazyDoubleLock.getInstance();
        SingletonLazyDoubleLock lazyDoubleLock1 = SingletonLazyDoubleLock.getInstance();

        // 通过反射实例
        Class<SingletonLazyDoubleLock> lazyClass = SingletonLazyDoubleLock.class;
        Constructor<SingletonLazyDoubleLock> constructor = lazyClass.getDeclaredConstructor();

        constructor.setAccessible(true);
        SingletonLazyDoubleLock lazyDoubleLock2 = constructor.newInstance();

        // 验证单例是否全局只有一个实例
        System.out.println("正常获取的实例比较:" + (lazyDoubleLock == lazyDoubleLock1));
        // 反射获取与正常获取对比
        System.out.println("正常获取的实例比较:" + (lazyDoubleLock == lazyDoubleLock2));

        // 输出相应对象
        System.out.println("lazyDoubleLock :" + lazyDoubleLock);
        System.out.println("lazyDoubleLock1:" + lazyDoubleLock1);
        System.out.println("lazyDoubleLock2:" + lazyDoubleLock2);
    }

    /**
     * 懒汉式 双重加锁
     */
    static class SingletonLazyDoubleLock {
        //
        public static SingletonLazyDoubleLock instance;

        // 私有构造函数
        private SingletonLazyDoubleLock() {
        }

        public static synchronized SingletonLazyDoubleLock getInstance() {
            if (instance == null) {
                synchronized (SingletonLazyDoubleLock.class) {
                    if (instance == null) {
                        instance = new SingletonLazyDoubleLock();
                    }
                }
            }

            return instance;
        }
    }
}

  • 结果:
正常获取的实例比较:true
正常获取的实例比较:false
lazyDoubleLock :singleton.SingletonDoubleCheckLockTest$SingletonLazyDoubleLock@4554617c
lazyDoubleLock1:singleton.SingletonDoubleCheckLockTest$SingletonLazyDoubleLock@4554617c
lazyDoubleLock2:singleton.SingletonDoubleCheckLockTest$SingletonLazyDoubleLock@74a14482

Process finished with exit code 0
结论:
  • 从结果可以看出,lazyDoubleLock 和 lazyDoubleLock2是正常获取的,是同一实例
  • lazyDoubleLock 通过反射获取的,与lazyDoubleLock 和 lazyDoubleLock2已经不是同一实例
  • 双重检查锁单例仍然可以被反射破坏

静态内部类

  • 代码演示:
package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author liouwb
 * 单例模式 静态内部类
 */
public class SingletonInnerTest {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 单例静态内部类
        SingletonInner inner = SingletonInner.getInstance();
        SingletonInner inner1 = SingletonInner.getInstance();

        // 通过反射实例
        Class<SingletonInner> lazyClass = SingletonInner.class;
        Constructor<SingletonInner> constructor = lazyClass.getDeclaredConstructor();

        constructor.setAccessible(true);
        SingletonInner inner2 = constructor.newInstance();

        // 验证单例是否全局只有一个实例
        System.out.println("正常获取的实例比较:" + (inner == inner1));
        // 反射获取与正常获取对比
        System.out.println("正常获取的实例比较:" + (inner == inner2));

        // 输出相应对象
        System.out.println("inner :" + inner);
        System.out.println("inner1:" + inner1);
        System.out.println("inner2:" + inner2);
    }

    /**
     * 单例静态内部类
     */
    static class SingletonInner {
        private SingletonInner() {
        }

        private static class SingletonHoler {
            private static SingletonInner INSTANCE = new SingletonInner();
        }

        public static final SingletonInner getInstance() {
            return SingletonHoler.INSTANCE;
        }

    }
}
  • 结果:
正常获取的实例比较:true
正常获取的实例比较:false
inner :singleton.SingletonInnerTest$SingletonInner@4554617c
inner1:singleton.SingletonInnerTest$SingletonInner@4554617c
inner2:singleton.SingletonInnerTest$SingletonInner@74a14482

Process finished with exit code 0
结论:
  • 被反射破坏

枚举单例

  • 代码演示:
package singleton;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * @author liouwb
 * 单例模式 枚举
 */
public class SingletonEnumTest {

    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 单例 枚举
        SingletonEnum singletonEnum = SingletonEnum.getInstance();
        SingletonEnum singletonEnum1 = SingletonEnum.getInstance();

        // 通过反射实例
        Class<SingletonEnum> singletonEnumClass = SingletonEnum.class;
        Constructor<SingletonEnum> constructor = singletonEnumClass.getDeclaredConstructor();

        constructor.setAccessible(true);
        SingletonEnum singletonEnum2 = constructor.newInstance();

        // 验证单例是否全局只有一个实例
        System.out.println("正常获取的实例比较:" + (singletonEnum == singletonEnum1));
        // 反射获取与正常获取对比
        System.out.println("正常获取的实例比较:" + (singletonEnum == singletonEnum2));

        // 输出相应对象
        System.out.println("singletonEnum :" + singletonEnum.hashCode());
        System.out.println("singletonEnum1:" + singletonEnum1.hashCode());
        System.out.println("singletonEnum2:" + singletonEnum2.hashCode());
    }


    /**
     * 枚举 单例
     */
    enum SingletonEnum {
        INSTANCE;

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

}
  • 结果:
Exception in thread "main" java.lang.NoSuchMethodException: singleton.SingletonEnumTest$SingletonEnum.<init>()
	at java.lang.Class.getConstructor0(Class.java:3074)
	at java.lang.Class.getDeclaredConstructor(Class.java:2170)
	at singleton.SingletonEnumTest.main(SingletonEnumTest.java:20)

Process finished with exit code 1
结论:
  • 通过反射获取枚举单例实例异常
  • 枚举形式的单例不能通过反射

防止反射破坏单例

  • 通过上述示例可以看出枚举可以防止单例破坏
  • 反射可以获取到单例类的私有构造,然后实例化
通过修改私有构造可以防止反射破坏单例
方式一、私有构造函数加锁
 // 私有构造函数
        private SingletonLazyLock() {
            synchronized (SingletonLazyLock.class) {
                if (instance != null) {
                    throw new RuntimeException("不要用反射破坏单例");
                }
            }
        }
  • 结果
    在这里插入图片描述
方式二、私有构造函数加标识位
  /**
     * 懒汉式
     */
    static class SingletonLazy {
        //
        public static SingletonLazy instance;

        private static boolean flag = false;

        // 私有构造函数
        private SingletonLazy() {
            synchronized (SingletonLazy.class) {
                if (!flag) {
                    flag = true;
                } else {
                    throw new RuntimeException("不要用反射破坏单例");
                }
            }
        }

        public static SingletonLazy getInstance() {
            if (instance == null) {
                instance = new SingletonLazy();
            }

            return instance;
        }
    }

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值