使用 readObject 和 writeObject 的 Java 自定义序列化

位置:首页>文章>详情   分类: Java教程 > 编程技术   阅读(348)   2023-06-26 07:54:18

在许多情况下,您可能需要在 java 中自定义序列化。例如,您有遗留的 Java 类,您出于任何原因都不愿意对其进行修改。也可能存在一些设计限制。或者甚至简单地说,该类预计会在未来的版本中发生变化,这可能会破坏先前序列化对象的反序列化 .

1. Java自定义序列化

在大多数情况下,当您自定义 java 序列化时,您将按顺序一个接一个地编写字段。它是覆盖默认 java 序列化 进程的最常见方式。

比方说,我们有一个 User 对象,我们想要自定义它的序列化过程。

public class User implements Serializable {

	private static final long serialVersionUID = 7829136421241571165L;
	
	private String firstName;
	private String lastName;
	private int accountNumber;
	private Date dateOpened;

	public User(String firstName, String lastName, int accountNumber, Date dateOpened) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.accountNumber = accountNumber;
		this.dateOpened = dateOpened;
	}
	
	public User() {
		super();
	}

	public final String getFirstName() {
		return firstName;
	}

	public final String getLastName() {
		return lastName;
	}

	public final int getAccountNumber() {
		return accountNumber;
	}

	public final Date getDateOpened() {
		return new Date(dateOpened.getTime());
	}

	public final void setFirstName(String aNewFirstName) {
		firstName = aNewFirstName;
	}

	public final void setLastName(String aNewLastName) {
		lastName = aNewLastName;
	}

	public final void setAccountNumber(int aNewAccountNumber) {
		accountNumber = aNewAccountNumber;
	}

	public final void setDateOpened(Date aNewDate) {
		Date newDate = new Date(aNewDate.getTime());
		dateOpened = newDate;
	}
}

1.1. readObject() 和 writeObject() 方法

要自定义序列化和反序列化,请在此类中定义 readObject()writeObject() 方法。

  • writeObject()方法中,使用ObjectOutputStream提供的writeXXX方法写入类属性。
  • readObject()方法中,使用ObjectInputStream提供的readXXX方法读取类属性。
  • 请注意,类属性在读取和写入方法中的顺序必须相同
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Date;

public class User implements Serializable {

	private static final long serialVersionUID = 7829136421241571165L;
	
	private String firstName;
	private String lastName;
	private int accountNumber;
	private Date dateOpened;

	public User(String firstName, String lastName, int accountNumber, Date dateOpened) {
		super();
		this.firstName = firstName;
		this.lastName = lastName;
		this.accountNumber = accountNumber;
		this.dateOpened = dateOpened;
	}
	
	public User() {
		super();
	}

	//Setters and Getters

	private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException 
	{		
		firstName = aInputStream.readUTF();
		lastName = aInputStream.readUTF();
		accountNumber = aInputStream.readInt();
		dateOpened = new Date(aInputStream.readLong());
	}

	private void writeObject(ObjectOutputStream aOutputStream) throws IOException 
	{
		aOutputStream.writeUTF(firstName);
		aOutputStream.writeUTF(lastName);
		aOutputStream.writeInt(accountNumber);
		aOutputStream.writeLong(dateOpened.getTime());
	}
}

现在让我们测试代码。

1.2.测试自定义序列化

package com.howtodoinjava.io.example;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Calendar;
import java.util.Date;

public class TestCustomSerialization 
{
	public static void main(String[] args) 
	{
		// Create new User object
		User myDetails = new User("Lokesh", "Gupta", 102825, new Date(Calendar.getInstance().getTimeInMillis()));

		// Serialization code
		try 
		{
			FileOutputStream fileOut = new FileOutputStream("User.ser");
			ObjectOutputStream out = new ObjectOutputStream(fileOut);
			out.writeObject(myDetails);
			out.close();
			fileOut.close();
		} 
		catch (IOException i) 
		{
			i.printStackTrace();
		}

		// De-serialization code
		User deserializedUser = null;
		try 
		{
			FileInputStream fileIn = new FileInputStream("User.ser");
			ObjectInputStream in = new ObjectInputStream(fileIn);
			deserializedUser = (User) in.readObject();
			in.close();
			fileIn.close();

			// verify the object state
			System.out.println(deserializedUser.getFirstName());
			System.out.println(deserializedUser.getLastName());
			System.out.println(deserializedUser.getAccountNumber());
			System.out.println(deserializedUser.getDateOpened());
		} 
		catch (IOException ioe) 
		{
			ioe.printStackTrace();
		} 
		catch (ClassNotFoundException cnfe) 
		{
			cnfe.printStackTrace();
		}
	}
}

//输出

Lokesh
Gupta
102825
Wed May 24 13:05:25 IST 2017

2.覆盖默认序列化以添加验证

有时您可能需要只执行任何特定的验证,或者在反序列化对象上运行一些业务规则——而不影响默认的 java 序列化机制。当您决定使用 readObject()writeObject() 方法时,这也是可能的。

在此用例中,您可以在 readObject()writeObject() 中使用 defaultReadObject()defaultWriteObject()方法——启用默认序列化和反序列化。然后,您可以在读/写方法中插入自定义验证或业务规则。 这样,JVM 将在默认序列化和反序列化过程发生后立即自动调用您的验证方法。

public class User implements Serializable {

	//class attributes, constructors, setters and getters as shown above

	/**
	 * Always treat de-serialization as a full-blown constructor, by validating the final state of the de-serialized object.
	 */
	private void readObject(ObjectInputStream aInputStream) throws ClassNotFoundException, IOException 
	{
		// perform the default de-serialization first
		aInputStream.defaultReadObject();

		// make defensive copy of the mutable Date field
		dateOpened = new Date(dateOpened.getTime());

		// ensure that object state has not been corrupted or tampered with malicious code
		//validateUserInfo();
	}

	/**
	 * This is the default implementation of writeObject. Customize as necessary.
	 */
	private void writeObject(ObjectOutputStream aOutputStream) throws IOException {
		
		//ensure that object is in desired state. Possibly run any business rules if applicable.
		//checkUserInfo();
		
		// perform the default serialization for all non-transient, non-static fields
		aOutputStream.defaultWriteObject();
	}
}

再次测试代码,您将看到以下输出:

Lokesh
Gupta
102825
Wed May 24 13:10:18 IST 2017

三、总结

正如我们所见,自定义序列化在 Java 中非常容易,它涉及非常简单的设计,即实现 readObject()writeObject() 方法;并添加任何额外的逻辑来支持应用程序业务逻辑。

尽管在大多数情况下默认序列化/反序列化就足够了;仍然需要时,您应在 Java 应用程序中使用自定义序列化。

在评论部分把你的问题告诉我。

快乐学习!!

地址:https://www.cundage.com/article/custom-serialization-readobject-writeobject.html

相关阅读

Java 序列化 是将对象转换为字节流的过程,因此我们可以执行诸如将其存储在磁盘上或通过网络发送之类的操作。反序列化是相反的过程——将字节流转换为内存中的对象。 在序列化期间,Java 运行时将...
Java 序列化 允许将 Java 对象写入文件系统以永久存储或在网络上传输到其他应用程序。 Java 中的序列化是通过Serializable 接口实现的。 Java Serializable...
在许多情况下,您可能需要在 java 中自定义序列化。例如,您有遗留的 Java 类,您出于任何原因都不愿意对其进行修改。也可能存在一些设计限制。或者甚至简单地说,该类预计会在未来的版本中发生变...
Java transient 关键字 用于类属性/变量,指示此类的序列化过程在为该类的任何实例创建持久字节流时应忽略此类变量。 瞬态变量 是不能序列化的变量。根据 Java 语言规范 [jls-...
我们都知道深度克隆(有一些性能开销)或深度复制的最简单方法是序列化。 Java 序列化涉及将对象序列化为字节,然后再从字节序列化为对象。 我会建议您在唯一需要时使用内存深度克隆,并且您不需要保留...
学习创建能够连接给定 Apache Kafka 代理实例的 spring boot 应用程序。此外,学习从 Kafka 主题生成和使用消息。 一、简介 在本教程中,我们将学习: 使用 Kafka...
了解 Java 中的记录类型。它在 Java 14 中作为预览功能引入,并在稍后完成。 Java 记录应该用作不可变的 POJO,以在类和应用程序之间传输数据。 1. 什么是记录? 和张举一样,...
Spring Boot JSON 教程展示了如何在 Spring Boot 注释中提供 JSON 数据。 春天 是一个流行的 Java 应用程序框架,弹簧贴 是 Spring 的演变,有助于创建...
学习使用 Jackson 库序列化和反序列化 Lombok @Builder 注释类。 出于演示目的,我们使用的是 Article 类,它的字段成员很少。我们在 Lombok @Builder ...
了解 Externalizable 与 Serializable 之间的区别 在两个方面很重要,一个 - 如果可以作为面试问题提出,第二个 - 您可以使用这些知识做出更明智的决策,以提高将序列化...