使用 `readResolve` 防止序列化破坏单例模式

单例模式是一种设计模式,其目的是确保一个类只有一个实例,并提供一个全局访问点。在 Java 中,我们常常通过私有化构造方法和提供静态访问方法来实现单例。然而,尽管这些手段可以有效防止类的实例化,反射和序列化依然能够破坏单例模式的唯一性。本文将重点讲解序列化如何破坏单例模式,以及如何通过 readResolve 方法来防止这种破坏。


1. 序列化和反序列化

序列化 是指将对象的状态转换为字节流,以便存储或传输;反序列化 则是将字节流恢复为对象的过程。

当一个单例对象被序列化并随后反序列化时,反序列化过程会创建一个新的对象。由于反序列化是通过从字节流恢复对象的属性状态,而不是通过调用构造方法,这导致反序列化后的对象与原始的单例实例不同。因此,反序列化过程实际上破坏了单例模式的约束。

2. 序列化如何破坏单例模式

假设我们有一个单例类如下:

import java.io.*;public class Singleton implements Serializable {private static final Singleton INSTANCE = new Singleton();private Singleton() {// 防止通过反射破解if (INSTANCE != null) {throw new RuntimeException("单例模式禁止反射调用!");}}public static Singleton getInstance() {return INSTANCE;}
}

现在让我们通过序列化和反序列化来测试这个单例类:

public class Main {public static void main(String[] args) throws Exception {Singleton instance1 = Singleton.getInstance();// 序列化对象ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("singleton.ser"));out.writeObject(instance1);out.close();// 反序列化对象ObjectInputStream in = new ObjectInputStream(new FileInputStream("singleton.ser"));Singleton instance2 = (Singleton) in.readObject();in.close();// 比较两个实例System.out.println(instance1 == instance2);  // 输出:false}
}

问题:反序列化出来的对象 instance2 与原单例对象 instance1 是不同的实例。这显然破坏了单例模式的唯一性。


3. 使用 readResolve 防止破坏

为了解决序列化对单例模式的破坏问题,Java 提供了 readResolve 方法。在反序列化时,如果类定义了 readResolve 方法,Java 会调用这个方法,并用该方法的返回值替换反序列化生成的新对象。这意味着我们可以通过 readResolve 返回已经存在的单例对象,从而防止反序列化创建新对象。

我们可以在 Singleton 类中添加 readResolve 方法:

private Object readResolve() {return INSTANCE;  // 返回当前已存在的单例实例
}
反序列化的执行流程:
  1. 反序列化时会创建一个新的对象。
  2. 在对象完全反序列化后,Java 会检查是否存在 readResolve 方法。如果有,则调用该方法。
  3. 该方法的返回值将替换反序列化生成的对象,从而确保仍然是同一个单例对象。

通过加入 readResolve 方法,程序的输出会变成:

System.out.println(instance1 == instance2);  // 输出:true
完整代码示例:
import java.io.*;public class Singleton implements Serializable {private static final Singleton INSTANCE = new Singleton();private Singleton() {if (INSTANCE != null) {throw new RuntimeException("单例模式禁止反射调用!");}}public static Singleton getInstance() {return INSTANCE;}// readResolve 方法防止反序列化破坏单例private Object readResolve() {return INSTANCE;  // 返回当前已存在的单例实例}
}

4. 为什么 readResolve 有效

readResolve 能防止序列化破坏单例的根本原因在于它的特殊调用机制。Java 在反序列化完成后自动调用类中的 readResolve,允许我们返回一个已有的实例,从而避免生成新的对象。在单例模式中,我们可以让 readResolve 方法返回类的单例对象 INSTANCE,这样即使经历序列化和反序列化,最终得到的对象仍然是同一个单例实例。

5. 其他注意事项

  • 防止反射破坏单例:尽管 readResolve 可以防止序列化破坏单例,但反射仍然能够通过调用私有构造方法来破坏单例。因此,建议在构造方法中添加逻辑,防止反射调用,如上文所示的 if (INSTANCE != null) 检查。

  • 序列化破坏的影响:当系统中有需要频繁序列化与反序列化的单例对象时,务必要考虑使用 readResolve 来确保单例模式的完整性,否则可能会导致多个实例并存,破坏系统设计。


6. 总结

在 Java 的单例模式中,序列化和反序列化可能会破坏单例的唯一性,而通过 readResolve 方法可以有效防止反序列化生成新对象,从而维护单例模式的约束。使用 readResolve 可以确保反序列化时返回的是现有的单例实例,而不是新的对象。对于需要持久化的单例类来说,这是一个非常重要的防御措施。

确保你的单例模式在序列化和反射攻击下都具备防御机制,才能真正做到一个类的实例唯一。

⚠️:搞来搞去都不太行,要么被反射破坏、要么被序列化破坏,都得自己写代码进行解决,那么有什么可以直接用的单例模式的实现方式呢?还真有,JVM已经给我们准备好了:枚举实现序列化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/53580.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

百度静态资源瓦片nginx直接显示完整案例

案例地址:https://download.csdn.net/download/jinhuding/89733763 访问显示效果:(根据瓦片地址直接显示) http://172.16.39.203:8099/tiles/

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant&am…

卷积神经网络综述

摘要 本文对卷积神经网络(Convolutional Neural Network,CNN)进行了全面综述。首先介绍了卷积神经网络的发展历程,包括早期的理论基础和关键突破。接着详细阐述了卷积神经网络的结构组成,包括卷积层、池化层、全连接层…

使用生成式AI解读人们的真实意图

你可以使用生成式AI来辨别别人所说的话与他们的真实意思,这是一种非常有用的工具,懂得如何明智地使用它非常重要。 你是否曾经怀疑过别人对你说的话是否真正传达了他们的真实意思? 我敢肯定你有过这种经历。 有时候,人们说的一…

ONU测试需要那些协议的学习

在进行ONU(Optical Network Unit,光网络单元)的相关测试时,需要学习和掌握一系列协议和技术,以确保测试的有效性和准确性。这些协议主要涉及网络传输、设备管理、服务质量(QoS)等方面。以下是需…

敏捷开发解决的到底是什么问题?

随着信息化社会的快速发展,软件项目的开发方式也面临着不断更新和改进的压力。敏捷开发作为一种新兴的软件开发方法,因其高效、灵活和适应市场需求的能力,逐渐得到了广泛的关注和应用。 一、敏捷开发是什么? 百度百科中是这样解…

视频号黄金时间

现在刷视频的人越来越多,看视频号的也很多,那我们应该怎样发视频号呢?发朋友圈都有黄金时间,那视频号有吗? 答案是:有的。 不同时间段发什么内容的视频,可以引流更多精准的流量,可…

期货量化现在是要比股票量化更适合高频交易,程序化交易

炒股自动化:申请官方API接口,散户也可以 python炒股自动化(0),申请券商API接口 python炒股自动化(1),量化交易接口区别 Python炒股自动化(2):获取…

【JAVA入门】Day34 - Stream流

【JAVA入门】Day34 - Stream流 文章目录 【JAVA入门】Day34 - Stream流一、Stream 流的作用和使用步骤1.Stream流的创建,数据的添加2. Stream流的中间方法3. Stream流的终结方法 Stream 流有什么作用?我们看一个例子: 【练习】需求&#xff…

记录一下linux安装nginx,也是很简单了啦

1、下载nginx 官网下载nginx:http://nginx.org/,这里很简单,下载自己想要的版本就行,这里不罗嗦 1、进入home目录,建一个文件夹nginx rootroot ~]# cd /home rootroot home]# mkdir nginx rootroot home]# cd /nginx2…

Java语言程序设计——篇十三(1)

🌿🌿🌿跟随博主脚步,从这里开始→博主主页🌿🌿🌿 欢迎大家:这里是我的学习笔记、总结知识的地方,喜欢的话请三连,有问题可以私信🌳🌳&…

系统架构师考试学习笔记第四篇——架构设计实践知识(21)安全架构设计理论与实践

本章考点: 第21课时主要学习信息系统中安全架构设计的理论和工作中的实践。根据考试大纲,本课时知识点会涉及案例分析题和论文题(各占25分),而在历年考试中,综合知识选择题目中也有过诸多考查。本课时内容侧重于知识点记忆;,按照以往的出题规律,安全架构设计基础知识…

SOMEIP_ETS_100: SD_ClientServiceActivate_no_FindServices_in_Main_Phase

测试目的: 确保客户端服务模式仅在启动阶段发送FindService消息,在主阶段不发送。 描述 本测试用例旨在验证DUT在客户端服务模式下的行为,即它应当在启动阶段发送FindService消息,并在进入主阶段后停止发送。 测试拓扑&#x…

【触想智能】工业一体机在物流领域上的四大应用分析

随着物流业的快速发展,工业一体机在物流领域上的应用越来越普遍。工业一体机是一种高级智能设备,是多种技术的综合应用,包括机械、电子、计算机、通讯等。 在物流行业中,工业一体机可以发挥其先进的技术和功能,提高物流…

基于单片机的仔猪喂饲系统设计

文章目录 前言资料获取设计介绍功能介绍设计清单具体实现截图参考文献设计获取 前言 💗博主介绍:✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师,一名热衷于单片机技术探索与分享的博主、专注于 精通51/STM32/MSP430/AVR等单片机设…

【网络安全 | 渗透工具】Cencys+Shodan使用教程

原创文章,不得转载。 文章目录 Cencys准备语法全文搜索字段和值搜索通配符搜索布尔逻辑搜索嵌套搜索时间相关搜索范围搜索双引号 (")转义序列和保留字符Censys 搜索语言中的主机查询查看主机搜索结果Censys 搜索语言中的证书查询查看证书搜索结果生成报告其余Shodan准备使…

【Java数据结构】泛型的进阶部分(泛型通配符)

1.❤️❤️前言~🥳🎉🎉🎉 Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。 如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的…

MT6895(天玑8100)处理器规格参数_MTK联发科平台方案

MT6895平台 采用台积电5nm工艺,与天玑 8000 相比性能提升 20% ,搭载4 个 2.85GHz A78 核心 4 个 2.0GHz A55 核心,CPU能效比上一代提高 25% 。GPU 采用了第三代的Valhall Arm Mali-G610 MC6架构,拥有6核心,搭配天玑81…

逻辑函数的公式化简法

目录 逻辑函数的公式化简法 并项法 吸收法 消去法 配项法 习题1 习题2 逻辑函数的公式化简法 并项法 B或B非结果为1,这样可以消去B,将两项合并为一项。 用于消去变量。 吸收法 1或上一个变量结果为1,1与上A变量结果为A变量。 1或任何…

随机生成车架号、统一社会信用代码和组织机构号

统一社会信用代码的具体赋码规则如下: 第一部分(第1位):登记管理部门代码。暂按国务院序列规则,5表示民政部门。 第二部分(第2位):机构类别代码。“1”表示社会团体、“2”表示民办非…