Пользовательская сериализация Java с использованием readObject() и writeObject()

Во многих случаях нам может понадобиться индивидуальная сериализация в java. Например, у нас есть устаревшие классы java, которые мы не хотим изменять по какой-либо причине. Также могут быть некоторые ограничения дизайна. Или даже просто класс, как ожидается, будет изменен в будущих выпусках, что может нарушить десериализацию ранее сериализованных объектов.

1. Пользовательская сериализация

В большинстве случаев, когда мы настраиваем сериализацию 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;}}

2. Реализация readObject() и writeObject()

Чтобы настроить сериализацию и десериализацию, определите методы readObject() и writeObject() в этом классе.

  • Внутри метода writeObject() запишите атрибуты класса, используя методы writeXXX, предоставляемые ObjectOutputStream.
  • Внутри метода readObject() прочитайте атрибуты класса, используя методы readXXX, предоставляемые ObjectInputStream.
  • Обратите внимание, что последовательность атрибутов класса в методах чтения и записи ДОЛЖНА БЫТЬ одинаковой.
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 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());}}

Теперь давайте проверим код.

3. Демонстрация

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 objectUser myDetails = new User("Lokesh", "Gupta", 102825, new Date(Calendar.getInstance().getTimeInMillis()));// Serialization codetry{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 codeUser 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 stateSystem.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();}}}

Вывод программы:

LokeshGupta102825Wed May 24 13:05:25 IST 2017

4. Проверка данных после сериализации

Иногда у нас могут быть требования, когда вы хотите выполнить только определенную проверку или запустить некоторые бизнес-правила для десериализованных объектов – не затрагивая механизм сериализации Java по умолчанию. Это также возможно, когда мы решаем использовать методы readObject() и writeObject().

В этом варианте использования вы можете использовать defaultReadObject() и defaultWriteObject() внутри методов readObject() и writeObject() – для включения сериализации и десериализации по умолчанию. И затем вы можете подключать свои собственные правила проверки или бизнес-правила внутри методов чтения/записи.

Таким образом, ваши методы проверки будут автоматически вызываться 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 firstaInputStream.defaultReadObject();// make defensive copy of the mutable Date fielddateOpened = 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 fieldsaOutputStream.defaultWriteObject();}}

Проверьте код еще раз, и вы увидите следующий результат:

LokeshGupta102825Wed May 24 13:10:18 IST 2017

5. Заключение

Как мы увидели, пользовательская сериализация в Java проста и требует очень простого проектирования, т. е. реализации методов readObject() и writeObject(), а также добавления любой дополнительной логики для поддержки бизнес-логики приложения.

Хотя в большинстве случаев сериализации/десериализации по умолчанию будет достаточно, при необходимости вам все же придется использовать пользовательскую сериализацию в приложениях Java.

Пишите мне свои вопросы в комментариях.

Прокрутить вверх