别写错了,你知道如何防止 反射 和 序列化 破坏单例模式吗?

>>强大,10k+点赞的 SpringBoot 后台管理系统竟然出了详细教程!

点击关注公众号,利用碎片时间学习

反射破坏单例

单例模式一般构造方法都是private,目的就是为了防止外界调用私有构造器创建多个实例,通过一个public的共有方法作为外界获取实例的唯一入口,从而实现单例。

但是反射能够访问私有的构造方法,只要反射获取的构造器调用setAccessible(true)方法即可。这样调用一次就会产生一个实例,调用多次就时多个实例,从而破坏单例。

如何防止:

只要在单例的私有构造器中添加判断单例是否已经构造的代码,如果单例之前已经构造,则抛出异常,如果没有构造,则无所谓。如下

//单例被破坏则抛异常
private SingleTon1() {
 if(singleTon1 != null) {
  throw new RuntimeException();
 }
}

单例完整代码:

public class SingleTon1 {
 private static SingleTon1 singleTon1 = new SingleTon1();
 
 private SingleTon1() {
  if(singleTon1 != null) {
   throw new RuntimeException();
  }
 }
 
 public static SingleTon1 getSingleTon1() {
  return singleTon1;
 }
}

测试:

反射测试代码:

public static void main(String[] args) throws Exception {
                Constructor<SingleTon1> constructor=SingleTon1.class.getDeclaredConstructor();
  constructor.setAccessible(true);
  SingleTon1 s1=constructor.newInstance();
  SingleTon1 s2=constructor.newInstance();
  System.out.println(s1);
  System.out.println(s2);
}

结果:抛出异常,阻止了调用私有构造器

Exception in thread "main" java.lang.reflect.InvocationTargetException
 at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
 at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
 at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
 at java.lang.reflect.Constructor.newInstance(Unknown Source)
 at singleTonDestory.test.main(test.java:21)
Caused by: java.lang.RuntimeException
 at singleTonDestory.SingleTon1.<init>(SingleTon1.java:13)
 ... 5 more

序列化破坏单例

添加readResolve方法就可以防止破坏单例

public class SingleTon1 implements Serializable{
 private static SingleTon1 singleTon1 = new SingleTon1();
 
 private SingleTon1() {
  if(singleTon1 != null) {
   throw new RuntimeException();
  }
 }
 public static SingleTon1 getSingleTon1() {
  return singleTon1;
 }
 
 //防止反序列化破坏单例,反序列化时,如果定义了readResolve方法,则直接返回吃方法指定的对象,而不需要单独再创建对象
 private Object readResolve() throws ObjectStreamException{
  return singleTon1;
 }
}

添加readResolve方法,反序列化时,会直接返回该方法指定的对象,不需要创建对象。

测试:

序列化测试代码:

public static void main(String[] args) throws Exception {
 //通过方法获取两个一样的单例
  SingleTon1 s1 = SingleTon1.getSingleTon1();
  SingleTon1 s2 = SingleTon1.getSingleTon1();
  
  //序列化到磁盘(保存对象)
  FileOutputStream fos = new FileOutputStream("d:/a.txt");
  ObjectOutputStream oos = new ObjectOutputStream(fos);
  oos.writeObject(s1);
  oos.close();
  fos.close();
 
  //从磁盘发序列化(获取对象)
  FileInputStream fis = new FileInputStream("D:/a.txt");
  ObjectInputStream ois = new ObjectInputStream(fis);
  SingleTon1 s= (SingleTon1) ois.readObject();
 
  System.out.println(s);
  System.out.println(s1);
  System.out.println(s2);
}

结果:

singleTonDestory.SingleTon1@5c647e05
singleTonDestory.SingleTon1@5c647e05
singleTonDestory.SingleTon1@5c647e05

从结果可以看出,防止了序列化破坏单例。

来源:blog.csdn.net/weixin_42130471/

article/details/89602999

推荐:

主流Java进阶技术(学习资料分享)

别写错了,你知道如何防止 反射 和 序列化 破坏单例模式吗?
PS:因为公众号平台更改了推送规则,如果不想错过内容,记得读完点一下“在看”,加个“星标”,这样每次新文章推送才会第一时间出现在你的订阅列表里。“在看”支持我们吧!  

原文始发于微信公众号(Java笔记虾):别写错了,你知道如何防止 反射 和 序列化 破坏单例模式吗?