千家信息网

Java中怎么破坏单例方式

发表于:2025-01-18 作者:千家信息网编辑
千家信息网最后更新 2025年01月18日,本篇内容介绍了"Java中怎么破坏单例方式"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!单例模式(S
千家信息网最后更新 2025年01月18日Java中怎么破坏单例方式

本篇内容介绍了"Java中怎么破坏单例方式"的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。是一种创建型设计模式。他的定义为:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

单例模式一般体现在类声明中,单例的类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

但是其实,单例并不是完完全全安全的,也是有可能被破坏的。

以下,是一次面试现场的还原,之所以会聊到这个话题,是因为面试官问了我很多关于单例模式的问题,我回答的还可以,之后面试官随口问了一句"单例绝对安全吗?",紧接着发生了如下对话:

Q:单例模式绝对安全吗?

A:(这个问题我知道,别想难倒我)不一定的,其实单例也是有可能被破坏的?

Q:哦?怎么说?

A:单例模式其实是对外隐藏了构造函数,保证用户无法主动创建对象。但是实际上我们是有办法可以破坏他的。

Q:那你知道有什么办法可以破坏单例吗??

A:有一个比较简单的方式,那就是反射。

反射破坏单例

我们先来一个比较常见的单例模式:

import java.io.Serializable;  /**   * 使用双重校验锁方式实现单例   */  public class Singleton implements Serializable{      private volatile static Singleton singleton;      private Singleton (){}      public static Singleton getSingleton() {          if (singleton == null) {              synchronized (Singleton.class) {                  if (singleton == null) {                      singleton = new Singleton();                  }              }          }          return singleton;      }  }

这个单例模式提供了一个private类型的构造函数,正常情况下,我们无法直接调用对象的私有方法。但是反射技术给我们提供了一个后门。

如下代码,我们通过反射的方式获取到Singleton的构造函数,设置其访问权限,然后通过该方法创建一个新的对象:

import java.lang.reflect.Constructor;  public class SingletonTest {      public static void main(String[] args) {          Singleton singleton = Singleton.getSingleton();          try {              Class singleClass = (Class)Class.forName("com.dev.interview.Singleton");              Constructor constructor = singleClass.getDeclaredConstructor(null);              constructor.setAccessible(true);              Singleton singletonByReflect = constructor.newInstance();              System.out.println("singleton : " + singleton);              System.out.println("singletonByReflect : " + singletonByReflect);              System.out.println("singleton == singletonByReflect : " + (singleton == singletonByReflect));          } catch (Exception e) {              e.printStackTrace();          }      }  }

输出结果为:

singleton : com.dev.interview.Singleton@55d56113  singletonByReflect : com.dev.interview.Singleton@148080bb  singleton == singletonByReflect : false

如上,通过发射的方式即可获取到一个新的单例对象,这就破坏了单例。

Q:那这种破坏单例的情况,有办法避免吗?

A:其实是可以的,只要我们在构造函数中加一些判断就行了。

如下方式,我们在Singleton的构造函数中增加如下代码:

private Singleton() {      if (singleton != null) {          throw new RuntimeException("Singleton constructor is called... ");      }  }

这样,在通过反射调用构造方法的时候,就会抛出异常:

Caused by: java.lang.RuntimeException: Singleton constructor is called...

序列化破坏单例

Q:嗯嗯,挺不错的,那我们换个问题吧。

A:(这部分面试官在犹豫问我什么问题,我主动提醒了他一句)其实,除了反射可以破坏单例,还有一种其他方式也可以的。

Q:嗯,那你就说说还有什么方式吧

A:其实通过序列化+反序列化的方式也是可以破坏单例的。

如以下代码,我们通过先将单例对象序列化后保存到临时文件中,然后再从临时文件中反序列化出来:

public class SingletonTest {      public static void main(String[] args) {          Singleton singleton = Singleton.getSingleton();          //Write Obj to file          ObjectOutputStream oos = null;          try {              oos = new ObjectOutputStream(new FileOutputStream("tempFile"));              oos.writeObject(singleton);              //Read Obj from file              File file = new File("tempFile");              ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));              Singleton singletonBySerialize = (Singleton)ois.readObject();              //判断是否是同一个对象             System.out.println("singleton : " + singleton);              System.out.println("singletonBySerialize : " + singletonBySerialize);              System.out.println("singleton == singletonBySerialize : " + (singleton == singletonBySerialize));          } catch (Exception e) {              e.printStackTrace();          }      }  }

输出结果如下:

singleton : com.dev.interview.Singleton@617faa95  singletonBySerialize : com.dev.interview.Singleton@5d76b067  singleton == singletonBySerialize : false

如上,通过先序列化再反序列化的方式,可获取到一个新的单例对象,这就破坏了单例。

因为在对象反序列化的过程中,序列化会通过反射调用无参数的构造方法创建一个新的对象,所以,通过反序列化也能破坏单例。

Q:那这种破坏单例的情况,也同样有办法避免吗?

A:当然也有了。只要修改下反序列化策略就好了。

只需要在Sinleton中增加readResolve方法,并在该方法中指定要返回的对象的生成策略几可以了。即序列化在Singleton类中增加以下代码即可:

private Object readResolve() {     return getSingleton();  }

Q:为什么增加readResolve就可以解决序列化破坏单例的问题了呢?

A:因为反序列化过程中,在反序列化执行过程中会执行到ObjectInputStream#readOrdinaryObject方法,这个方法会判断对象是否包含readResolve方法,如果包含的话会直接调用这个方法获得对象实例。

Q:那如果没有readResolve方法的话,反序列化的时候会怎么创建对象呢?

A:当然也是反射咯。

Q:那前面不是说使用反射的情况,直接在构造函数抛异常不就行了吗?

A:这个我还真试过,其实是不行的,反序列化使用的反射构造器和我们代码中使用反射的构造器不是同一个,反序列化用到的构造器并不会调用到我们对象中的构造函数…balabala…(我也不知道面试官听不听得懂,感觉是没听懂…)

Q:哦。OK吧,请问你什么时候可以来上班?

不久之后,我入职了这家公司,在一次和当初的面试官聊天的时候,他无意间和我说:当时我面试你的时候,关于单例的破坏那几个问题,其实最开始我只是随口一问,没想到你给我吹水了20分钟…当时我就觉得你这家伙是个可造之材。

"Java中怎么破坏单例方式"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注网站,小编将为大家输出更多高质量的实用文章!

对象 序列 方式 方法 反射 模式 函数 代码 情况 时候 问题 办法 过程 安全 实例 构造器 输出 主动 内容 如上 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 世界共有多少文献数据库 计算机网络技术开发技术服务 软件开发学习哪个编程语言好 开展网络安全业务培训意义 计算机网络技术结课小结 如何用网址查询服务器 数据库查询优化方法面试题 河北技术软件开发现价 新余高性价比服务器哪家靠谱 深圳网络技术科技公司有哪些 上海晒尔网络技术有限公司 网络技术可以拓展哪些行业 教师网络安全十不准 西藏数据库通用多路锁控板方案 吉林网络安全信息 学习课件服务器出错 天天狼人杀服务器维护 方舟无法读取服务器 郴州电脑软件开发学费多少 自己做一个网络服务器 宁波应用软件开发公司 搜网络技术有限公司 武汉网络技术员招聘 网络安全手抄报评选通知 App 带数据库如何发布 婺城区软件开发 战神引擎复制到另外服务器 数据库sql文件怎么查看 近两年互联网科技发展 dell服务器序列号在哪里找
0