当前位置: 首页 > ops >正文

20.java反序列化-对象的类自定义的readObject()方法

20. java反序列化-对象的类自定义的readObject()方法

在 Java 中,反序列化时调用自定义的 readObject 方法是由 ObjectInputStreamreadObject 方法触发的。
具体来说,ObjectInputStream 在反序列化一个对象时,会检查该对象的类是否实现了 Serializable 接口,并且是否定义了自定义的 readObject 方法。
如果满足这些条件,ObjectInputStream 就会调用自定义的 readObject 方法来处理对象的反序列化。

调用流程

  1. 反序列化触发

    • 当你调用 ObjectInputStream.readObject() 方法时,反序列化过程开始。
    • ObjectInputStream 会读取序列化数据流中的对象信息。
  2. 类检查

    • ObjectInputStream 会检查反序列化的对象所属的类是否实现了 Serializable 接口。
    • 如果类没有实现 Serializable 接口,会抛出 NotSerializableException
  3. 查找 readObject 方法

    • ObjectInputStream 会通过反射检查目标类是否定义了自定义的 readObject 方法。
    • 自定义的 readObject 方法需要满足以下签名:
      private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
      
  4. 调用 readObject 方法

    • 如果找到了自定义的 readObject 方法,ObjectInputStream 会调用它来处理对象的反序列化。
    • readObject 方法中,你可以手动读取序列化数据流中的数据,并初始化对象的字段。
    • 在自定义的 readObject 方法中,通常需要调用 in.defaultReadObject() 来处理非 transient 字段的反序列化。
    • 对于 transient 字段或需要特殊处理的字段,可以在 readObject 方法中手动初始化。
  5. 默认反序列化

    • 如果没有定义自定义的 readObject 方法,ObjectInputStream 会使用默认的反序列化机制,即通过反射直接设置对象的字段值。

总结

  • 自定义的 readObject 方法在反序列化过程中被调用,前提是类实现了 Serializable 接口,并且定义了该方法。
  • ObjectInputStream 通过反射查找并调用自定义的 readObject 方法。
  • 自定义的 readObject 方法允许你手动控制反序列化的过程,特别是对于 transient 字段或需要特殊处理的字段。

示例代码

import java.io.*;
import java.util.Date;/*** 主程序,演示序列化和反序列化操作*/
public class Main {// 序列化文件路径private static final String SERIALIZED_FILE = "user.ser";public static void main(String[] args) throws IOException, ClassNotFoundException{// 1. 创建用户对象User user = new User("小红", "securePassword123", 30, new Date());System.out.println("原始对象: " + user);// 2. 序列化对象到文件SerializationUtils.serialize(user, SERIALIZED_FILE);System.out.println("-------------------下面执行反序列化--------------------");// 3. 反序列化对象User deserializedUser = (User) SerializationUtils.deserialize(SERIALIZED_FILE);System.out.println("反序列化后的对象: " + deserializedUser);// 注意: password字段被标记为transient,不会被序列化System.out.println("原始对象密码: " + user.getPassword());System.out.println("反序列化对象密码: " + deserializedUser.getPassword()); // 输出null}
}/*** 用户实体类,实现Serializable接口以支持序列化*/
class User implements Serializable {// 序列化版本UID,用于版本控制private static final long serialVersionUID = 1L;private String username;private transient String password; // transient字段不会被序列化private int age;private Date registerDate;// 构造方法public User() {System.out.println("调用User类的无参构造函数");}public User(String username, String password, int age, Date registerDate) {System.out.println("调用User类的有参构造函数");this.username = username;this.password = password;this.age = age;this.registerDate = registerDate;}// Getter和Setter方法public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Date getRegisterDate() {return registerDate;}public void setRegisterDate(Date registerDate) {this.registerDate = registerDate;}@Overridepublic String toString() {return "User{" +"username='" + username + '\'' +", age=" + age +", registerDate=" + registerDate+", 被transient 标记的字段password="+password+'}';}// 自定义反序列化方法private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {System.out.println("调用了User类自定义的readObject方法");// 调用默认的反序列化方法,初始化非transient字段ois.defaultReadObject();// 自定义逻辑:初始化transient字段(如果需要)// 例如,从其他字段或外部资源恢复passwordthis.password = "defaultPassword"; // 示例:设置默认密码}
}/*** 序列化和反序列化工具类*/
class SerializationUtils {/*** 将对象序列化到文件** @param obj      要序列化的对象* @param filePath 文件路径* @throws IOException 如果发生I/O错误*/public static void serialize(Object obj, String filePath) throws IOException {try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath))) {oos.writeObject(obj);System.out.println("对象序列化成功,保存到: " + filePath);}}/*** 从文件反序列化对象** @param filePath 文件路径* @return 反序列化后的对象* @throws IOException            如果发生I/O错误* @throws ClassNotFoundException 如果类未找到*/public static Object deserialize(String filePath) throws IOException, ClassNotFoundException {try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath))) {Object obj = ois.readObject();System.out.println("对象反序列化成功,从: " + filePath);return obj;}}
}

运行结果

在User类自定义的readObject方法处下断点,Debug运行,函数调用栈显示,调用了User类自定义的readObject方法
在这里插入图片描述
在这里插入图片描述
并且发现,反序列化过程中,并不会调用User类的任何构造函数
在这里插入图片描述

总结

  • Serializable 接口检查ObjectInputStream 通过反射获取目标类的 Class 对象,并检查是否实现了 Serializable 接口。
  • 自定义 readObject 方法检查:通过 ObjectStreamClass 描述符检查类是否定义了自定义的 readObject 方法。
  • 反射调用:如果定义了自定义的 readObject 方法,ObjectInputStream 会通过反射调用该方法。
  • 默认反序列化:如果未定义自定义的 readObject 方法,使用默认的反序列化机制。

这一过程是 Java 序列化机制的核心部分,确保了对象能够正确地被反序列化,并支持自定义的反序列化逻辑。

http://www.xdnf.cn/news/5020.html

相关文章:

  • 【算法】:滑动窗口
  • C++八股 —— vector底层
  • OpenHarmony launcher开发——删除dock栏
  • MegaCLI Raid管理工具
  • 如何训练deepseek语言大模型
  • 数据结构(1)复杂度
  • QT事件介绍及实现字体放大缩小(滚轮)
  • LIMIT详解
  • 在一个FreeBSD的控制台,当移动鼠标时,就会有字符发生,这是怎么回事:35;62;18m35;60;
  • 【PhysUnits】3.2 SI 量纲 补充幂运算(dimension/mod.rs)
  • 低成本自动化改造的18个技术锚点深度解析
  • STM32-TIM定时中断(6)
  • MOS管的优缺点及应用
  • CMA认证对象?CMA评审依据,CMA认证好处
  • python打卡day20
  • C++ —— 类的嵌套和循环依赖问题
  • SCDN是什么?
  • 如何避免双击时触发单击事件
  • 自然语言处理 (NLP) 技术发展:从规则到大型语言模型的演进之路
  • C++ 引用传参机制
  • Oracle Fusion常用表
  • AD 绘制原理图--元件的放置
  • 大模型的实践应用39-Qwen3(72B)+langchain框架+MCP(大模型上下文协议)+RAG+传统算法等研发数学教学管理与成绩提升系统
  • 计算几何图形算法经典问题整理
  • 卡洛诗的“破”与“立”
  • RDD转换算子案例
  • 我的AD快捷键方案【留存】
  • C++ -- string
  • 裸机上的 printf:在无操作系统环境下构建 C 标准库
  • 《工业计算机硬件技术支持手册》适用于哪些人群?