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

使用readObjectwriteObject的 Java 自定义序列化

原文: https://howtodoinjava.com/java/serialization/custom-serialization-readobject-writeobject/

在某些情况下,您可能需要在 Java 中使用自定义序列化。 例如,您有遗留的 Java 类,由于任何原因都不愿意对其进行修改。 也可能存在一些设计约束。 甚至简单地说,该类将在将来的发行版中进行更改,这可能会破坏先前序列化对象的反序列化

Table of Contents

1\. Custom Serialization
2\. Default Serialization with Added Validation
3\. Summary

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

3. 总结

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

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

将我的问题放在评论部分。

学习愉快!