设计模式——单例模式(Singleton),以及防破解

代码:GitHub

0. UML结构图

在这里插入图片描述

1. 四种安全的单例示例

1.1 单例-饿汉式

/**
 * 单例-饿汉式
 *
 * @author ALion
 * @version 2019/1/25 21:22
 */
public class SingletonDemo01 {

    // 类初始化,立即加载这个对象,天然线程安全,不过无法延时加载
    private static SingletonDemo01 instance = new SingletonDemo01();

    // 私有,防止直接实例化(不过仍会被反射实例化)
    private SingletonDemo01() {
    }

    // 无锁,效率高
    public static SingletonDemo01 getInstance() {
        return instance;
    }

}

1.2 单例-懒汉式(双重校验锁)

/**
 * 单例-懒汉式(双重校验锁)
 *
 * @author ALion
 * @version 2019/1/25 21:22
 */
public class SingletonDemo02 {

  	// volatile防止优化器对指令重排序(会导致线程不安全)
    // 原先创建顺序:1.对象半始化 2.调用对象init方法,完成初始化 3.获取到对象
    // JIT优化重排序后:1.对象半始化 2.获取到对象 3.调用对象init方法,完成初始化
    // 解释: 重排序后,获取到的对象是半初始化的对象,直接使用可能导致部分问题
    private static volatile SingletonDemo02 instance;

    // 私有,防止直接实例化(不过仍会被反射实例化)
    private SingletonDemo02() {
    }

    // 懒加载
    public static SingletonDemo02 getInstance() {
        // 先判断是否为null,可以提高效率,防止后续获取对象时,直接被锁住(消耗时间)
        // 注:网上有人直接在方法上加synchronized,忽略了这个优化点
        if (instance == null) {
            synchronized (SingletonDemo02.class) {
                // 防止重复创建对象
                if (instance == null) {
                    instance = new SingletonDemo02();
                }
            }
        }
        return instance;
    }

}

1.3 单例-静态内部类(推荐)

/**
 * 单例-静态内部类(推荐)
 * <p>
 *     优点:懒加载、高效
 * </p>
 * @author ALion
 * @version 2019/1/25 21:22
 */
public class SingletonDemo03 {

    // 私有,防止直接实例化(不过仍会被反射实例化)
    private SingletonDemo03() {
    }

    // 懒加载,只有该方法被调用,才会加载(因为只有静态内部类被调用,才会实例化该对象)
    public static SingletonDemo03 getInstance() {
        return InnerClass.instance;
    }

    // 静态内部类保证多线程加载的安全性
    private static class InnerClass {
        private static final SingletonDemo03 instance = new SingletonDemo03();
    }

}

1.4 单例-枚举(推荐)

/**
 * 单例-枚举(推荐)
 * <p>
 *     优点:简洁、安全、高效、直接能避免反射和反序列化的漏洞
 * </p>
 * @author ALion
 * @version 2019/1/25 21:40
 */
public enum SingletonDemo04 {

    // 直接调用SingletonDemo04.INSTANCE即可
    INSTANCE;

    // 定义对象的属性、方法

}

1.5 4种单例的测试

/**
 * 4种单例的测试
 *
 * @author ALion
 * @version 2019/1/25 22:10
 */
public class SingletonTest {

    public static void main(String[] args) {
        // 1.测试 -> 单例-饿汉式
        SingletonDemo01 instance1 = SingletonDemo01.getInstance();
        SingletonDemo01 instance2 = SingletonDemo01.getInstance();
        System.out.println(instance1 == instance2);

        // 2.测试 -> 单例-懒汉式(双重校验锁)
        SingletonDemo02 instance3 = SingletonDemo02.getInstance();
        SingletonDemo02 instance4 = SingletonDemo02.getInstance();
        System.out.println(instance3 == instance4);

        // 3.测试 -> 单例-静态内部类
        SingletonDemo03 instance5 = SingletonDemo03.getInstance();
        SingletonDemo03 instance6 = SingletonDemo03.getInstance();
        System.out.println(instance5 == instance6);

        // 4.测试 -> 单例-枚举
        SingletonDemo04 instance7 = SingletonDemo04.INSTANCE;
        SingletonDemo04 instance8 = SingletonDemo04.INSTANCE;
        System.out.println(instance7 == instance8);

    }

}

2. 单例防破解

2.1 防止反射、反序列化的单例Code示例

import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * 单例-防止反射、反序列化
 *
 * @author ALion
 * @version 2019/1/25 21:22
 */
public class SingletonDemo05 implements Serializable {

    private static volatile SingletonDemo05 instance;

    private SingletonDemo05() {
        // 防止反射实例化多个对象
        if (instance != null) {
            throw new RuntimeException("只能实例化一个对象");
        }
    }

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

    // 防止反序列化
    private Object readResolve() throws ObjectStreamException {
        return instance;
    }

}

2.2 带可序列化接口的单例Code示例(附加的测试用类)

public class SingletonDemo02Serializable implements Serializable {

    private static SingletonDemo02Serializable instance;

    private SingletonDemo02Serializable() {
    }

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

}

2.3 单例防破解测试

import java.io.*;
import java.lang.reflect.Constructor;

/**
 * 防止反射、反序列化破解单例的测试
 *
 * @author ALion
 * @version 2019/1/25 22:45
 */
public class AntiCrashSingletonTest {

    public static void main(String[] args) {
        // 测试反射
        reflect();

        // 测试反序列化
        unserializable();
    }

    public static void reflect() {
        // 1.测试 -> 反射破解单例
        try {
            SingletonDemo02 o1 = SingletonDemo02.getInstance();

            Class<SingletonDemo02> clazz = (Class<SingletonDemo02>) Class.forName("com.skey.designpattern.singleton.SingletonDemo02");
            Constructor<SingletonDemo02> constructor = clazz.getDeclaredConstructor(null);
            constructor.setAccessible(true);
            SingletonDemo02 o2 = constructor.newInstance();
            System.out.println(o1 == o2);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 2.测试 -> 单例-防止反射破解
        try {
            SingletonDemo05 o3 = SingletonDemo05.getInstance();

            Class<SingletonDemo05> clazz05 = (Class<SingletonDemo05>) Class.forName("com.skey.designpattern.singleton.SingletonDemo05");
            Constructor<SingletonDemo05> constructor05 = clazz05.getDeclaredConstructor(null);
            constructor05.setAccessible(true);
            // 当对象已经实例化了后,再用反射去实例化,会报异常
            SingletonDemo05 o4 = constructor05.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void unserializable() {
        // 3.测试 -> 反序列化破解单例
        String path1 = "./test1.txt";
        SingletonDemo02Serializable o1 = SingletonDemo02Serializable.getInstance();
        try (FileOutputStream fos = new FileOutputStream(path1);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(o1);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try(FileInputStream fis = new FileInputStream(path1);
            ObjectInputStream ois = new ObjectInputStream(fis)) {
            SingletonDemo02Serializable o2 = (SingletonDemo02Serializable) ois.readObject();
            // 判断是否是同一个对象
            System.out.println(o2 == o1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 4.测试 -> 单例-防止反序列化破解
        String path2 = "./test2.txt";
        SingletonDemo05 o3 = SingletonDemo05.getInstance();
        try (FileOutputStream fos = new FileOutputStream(path2);
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(o3);
        } catch (IOException e) {
            e.printStackTrace();
        }

        try(FileInputStream fis = new FileInputStream(path2);
            ObjectInputStream ois = new ObjectInputStream(fis)) {
            SingletonDemo05 o4 = (SingletonDemo05) ois.readObject();
            // 判断是否是同一个对象
            System.out.println(o4 == o3);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值