
2022-08-07
137
原创
破坏单例模式
首先,我先给出一种单例模式的实现方式,用静态内部类去实现单例模式
public class Singleton implements Serializable{
// 私有构造方法
private Singleton(){}
private static class SingletonHolder {
private static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
}
我们设计单例模式的意义就是对于某个类只能创建一个对象,但是现在我们可以通过序列化和反射的机制,去破坏单例模式,从而创建出不同的对象
序列化
public class Client {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//writeObject2File();
readObjectFromFile();
readObjectFromFile();
}
// 读取文件中的数据
public static void readObjectFromFile() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\刘柄岐\\Desktop\\a.txt"));
Singleton instance = (Singleton) ois.readObject();
System.out.println(instance);
ois.close();
}
//向文件中写数据(对象)
public static void writeObject2File() throws IOException {
Singleton instance = Singleton.getInstance();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\刘柄岐\\Desktop\\a.txt"));
oos.writeObject(instance);
//释放资源
oos.close();
}
}
控制台打印
com.lbq.pattern.singleton.demo3.Singleton@5b6f7412
com.lbq.pattern.singleton.demo3.Singleton@27973e9b
Process finished with exit code 0
可以看出来,通过序列化再反序列化创建出来的对象,是不同的对象
解决方法
在Singleton类中添加readResolve()方法,在反序列化时被调用,如果定义了这个方法,就返回这个方法的值,如果没有,则返回新new出来的对象
//当进行反序列化时,会自动调用该方法,将该方法的返回值直接返回
public Object readResolve() {
return SingletonHolder.instance;
}
这样反序列化生成的对象就还是同一个了
反射
public static void main(String[] args) throws Exception{
// 1.获取Singleton的字节码对象
Class clazz = Singleton.class;
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
Singleton s1 = (Singleton) cons.newInstance();
Singleton s2 = (Singleton) cons.newInstance();
// 输出为false,所以破化了单例模式
System.out.println(s1 == s2);
}
通过反射获取其私有构造方法,去创建对象从而破坏了单例模式
解决方法
反射能够破坏单例模式,是因为它可以调用其构造方法,就是是private也没用,放射一样能调用,那么我们可以在它的构造方法里面,写一些逻辑,判断是否是第一次创建,如果不是第一次直接抛异常
private static boolean flag = false;
// 私有构造方法
private Singleton(){
synchronized (Singleton.class) {
//判断flag的值是否是true,如果是true,说明非第一次访问,直接抛出异常,如果是false的话,说明是第一次
if (flag) {
throw new RuntimeException("单例对象不能创建多个");
}
flag = true;
}
}
单例模式常见的一些实现,比如双重检测锁,静态内部类,都是可以通过序列化和反序列化去破坏的,而且往往这些实现方式,设计比较复杂,官方其实推荐我们用另一种实现,枚举,枚举的实现设计简单,没有线程安全问题,而且序列化和反射也是破坏不了的
枚举实现如下
public enum Singleton {
INSTANCE;
}
没了!!简单吧
Java