设计模式之破环单例模式和阻止破坏

目录

  • [1. 序列化和反序列化](#1. 序列化和反序列化)
  • [2. 反射](#2. 反射)

这里单例模式就不多说了
23种设计模式之单例模式

1. 序列化和反序列化

这里用饿汉式来做例子
LazySingleton

java 复制代码
import java.io.Serializable;

public class LazySingleton implements Serializable {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {}

    public static synchronized LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }
}

TestSerializer

java 复制代码
import java.io.*;

public class TestSerializer {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 懒汉式
        LazySingleton instance = LazySingleton.getInstance();

        // 通过序列化和反序列化的方式,创建对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton"));
        oos.writeObject(instance);

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("singleton")));
        LazySingleton objInstance = (LazySingleton)ois.readObject();

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

输出结果:

复制代码
yxz.singleton.LazySingleton@3764951d
yxz.singleton.LazySingleton@312b1dae

可以看到这俩对象不一样。

但是使用枚举输出结果是一样的。

如果不让其破坏呢???

我们在单例中添加一个方法。

java 复制代码
import java.io.Serializable;

public class LazySingleton implements Serializable {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {}

    public static synchronized LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

    private Object readResolve() {
    	return lazySingleton;
    }
}

输出结果:

复制代码
yxz.singleton.LazySingleton@3764951d
yxz.singleton.LazySingleton@3764951d

这样输出结果是一样的了。因为反序列化创建对象时,是通过反射创建的,反射会调用我们自己的readResolve方法,如果重写,会调用这个,否则会破坏单例模式。

2. 反射

通过字节码对象创建构造器对象,通过构造器对象,初始化单例对象,由于单例对象的构造方法是私有化的,调用构造器中的方法,赋予权限,创建单例对象。

TestReflect

java 复制代码
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestReflect {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
    	
        Class clz = LazySingleton.class;
		// 通过字节码对象创建构造器对象
        Constructor constructor = clz.getDeclaredConstructor();
        // 赋予权限
        constructor.setAccessible(true);
        // 初始化单例对象
        LazySingleton clzInstance = (LazySingleton)constructor.newInstance();
        System.out.println(clzInstance);

        LazySingleton instance = LazySingleton.getInstance();
        System.out.println(instance);
    }
}

输出结果:

复制代码
yxz.singleton.LazySingleton@1b6d3586
yxz.singleton.LazySingleton@4554617c

坏了,又是一样的,这可怎么办!!!

我们如何阻止呢?

我们对代码的构造模式进行修改

java 复制代码
import java.io.Serializable;

public class LazySingleton implements Serializable {
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {
        if(lazySingleton != null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    public static synchronized LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

    private Object readResolve() {
    	return lazySingleton;
    }
}

这样我们再运行,输出结果是:

复制代码
yxz.singleton.LazySingleton@1b6d3586
yxz.singleton.LazySingleton@4554617c

恭喜你,仍然能出现两个!!!但是,我们先创建一个对象,再使用反射呢?

java 复制代码
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TestReflect {
    public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        LazySingleton instance = LazySingleton.getInstance();
        System.out.println(instance);
        Class clz = LazySingleton.class;

        Constructor constructor = clz.getDeclaredConstructor();
        constructor.setAccessible(true);
        LazySingleton clzInstance = (LazySingleton)constructor.newInstance();
        System.out.println(clzInstance);
    }
}

运行结果:

java 复制代码
yxz.singleton.LazySingleton@1b6d3586
Exception in thread "main" java.lang.reflect.InvocationTargetException
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at yxz.singleton.broker.TestReflect.main(TestReflect.java:16)
Caused by: java.lang.RuntimeException: 不允许创建多个实例
	at yxz.singleton.LazySingleton.<init>(LazySingleton.java:10)
	... 5 more

这才没有创建多个。

当然,我建议是使用枚举来组织它,不过,我在使用的时候,还是没怎怎么故意使用反射或者序列化这些来破坏单例模式。

相关推荐
Carl_奕然8 小时前
【智能体】Agent的四种设计模式之:ReAct
人工智能·设计模式·语言模型
二哈赛车手10 小时前
新人笔记---多策略搭建策略执行链实现RAG检索后过滤
java·笔记·spring·设计模式·ai·策略模式
楼田莉子11 小时前
仿Muduo的高并发服务器:Channel模块与Poller模块
linux·服务器·c++·学习·设计模式
geovindu1 天前
go: Strategy Pattern
开发语言·设计模式·golang·策略模式
嵌入式学习_force1 天前
02_state
设计模式·蓝牙
qcx231 天前
Warp源码深度解析(七):Token预算策略——双轨计费、上下文溢出与摘要压缩
人工智能·设计模式·rust·wrap
Cosolar2 天前
提示词工程面试题系列 - Zero-Shot Prompting 和 Few-Shot Prompting 的核心区别是什么?
人工智能·设计模式·架构
geovindu2 天前
go:Template Method Pattern
开发语言·后端·设计模式·golang·模板方法模式
钝挫力PROGRAMER2 天前
贫血模型的改进
java·开发语言·设计模式·架构
qcx232 天前
Warp源码深度解析(二):自研GPU UI框架——WarpUI的ECH模式与渲染管线
人工智能·ui·设计模式·rust