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

> 原文： [https://howtodoinjava.com/java/serialization/custom-serialization-readobject-writeobject/](https://howtodoinjava.com/java/serialization/custom-serialization-readobject-writeobject/)

在某些情况下，您可能需要在 Java 中使用**自定义序列化**。 例如，您有遗留的 Java 类，由于任何原因都不愿意对其进行修改。 也可能存在一些设计约束。 甚至简单地说，该类将在将来的发行版中进行更改，这可能会破坏[先前序列化对象的反序列化](https://howtodoinjava.com/java/serialization/a-mini-guide-for-implementing-serializable-interface-in-java/)。

```java
Table of Contents

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

## 1\. Java 自定义序列化

在大多数情况下，当**自定义 Java 序列化**时，您将按顺序逐一写入字段。 它最常用的*方法将覆盖默认的 Java 序列化*进程。

假设我们有一个`User`对象，我们想自定义它的序列化过程。

```java
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`方法读取类属性。
*   请注意，读写方法中类属性的**序列必须与**相同。

```java
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 测试自定义序列化

```java
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();
		}
	}
}

```

//输出

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

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

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

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

```java
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();
	}
}

```

再次测试代码，您将看到以下输出：

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

## 3\. 总结

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

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

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

学习愉快！