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

目录

  • [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

这才没有创建多个。

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

相关推荐
鱼跃鹰飞8 小时前
设计模式系列:工厂模式
java·设计模式·系统架构
老蒋每日coding15 小时前
AI Agent 设计模式系列(十九)—— 评估和监控模式
人工智能·设计模式
会员果汁16 小时前
23.设计模式-解释器模式
设计模式·解释器模式
「QT(C++)开发工程师」1 天前
C++设计模式
开发语言·c++·设计模式
茶本无香1 天前
设计模式之七—装饰模式(Decorator Pattern)
java·设计模式·装饰器模式
漂洋过海的鱼儿1 天前
设计模式——EIT构型(三)
java·网络·设计模式
老蒋每日coding2 天前
AI Agent 设计模式系列(十八)—— 安全模式
人工智能·安全·设计模式
老蒋每日coding2 天前
AI Agent 设计模式系列(十六)—— 资源感知优化设计模式
人工智能·设计模式·langchain
老蒋每日coding2 天前
AI Agent 设计模式系列(十七)—— 推理设计模式
人工智能·设计模式
冷崖2 天前
桥模式-结构型
c++·设计模式