20.java反序列化-对象的类自定义的readObject()方法
20. java反序列化-对象的类自定义的readObject()方法
在 Java 中,反序列化时调用自定义的 readObject
方法是由 ObjectInputStream
的 readObject
方法触发的。
具体来说,ObjectInputStream
在反序列化一个对象时,会检查该对象的类是否实现了 Serializable
接口,并且是否定义了自定义的 readObject
方法。
如果满足这些条件,ObjectInputStream
就会调用自定义的 readObject
方法来处理对象的反序列化。
调用流程
-
反序列化触发:
- 当你调用
ObjectInputStream.readObject()
方法时,反序列化过程开始。 ObjectInputStream
会读取序列化数据流中的对象信息。
- 当你调用
-
类检查:
ObjectInputStream
会检查反序列化的对象所属的类是否实现了Serializable
接口。- 如果类没有实现
Serializable
接口,会抛出NotSerializableException
。
-
查找
readObject
方法:ObjectInputStream
会通过反射检查目标类是否定义了自定义的readObject
方法。- 自定义的
readObject
方法需要满足以下签名:private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;
-
调用
readObject
方法:- 如果找到了自定义的
readObject
方法,ObjectInputStream
会调用它来处理对象的反序列化。 - 在
readObject
方法中,你可以手动读取序列化数据流中的数据,并初始化对象的字段。 - 在自定义的
readObject
方法中,通常需要调用in.defaultReadObject()
来处理非transient
字段的反序列化。 - 对于
transient
字段或需要特殊处理的字段,可以在readObject
方法中手动初始化。
- 如果找到了自定义的
-
默认反序列化:
- 如果没有定义自定义的
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 序列化机制的核心部分,确保了对象能够正确地被反序列化,并支持自定义的反序列化逻辑。