使用不可序列化的属性序列化Java对象

人们可能有多种原因想要使用自定义序列化而不是依赖Java的默认序列化。 最常见的原因之一是为了提高性能,但是编写自定义序列化的另一个原因是不支持默认序列化机制。 具体来说,如本博文所述,自定义序列化可用于允许将较大的对象序列化,即使该对象的属性本身不能直接序列化也是如此

下一个代码清单显示了一个简单的类,该类用于将给定类序列化为提供名称的文件,并从该文件中反序列化对象。 我将在本文中使用它来演示序列化。

SerializationDemonstrator.java

package dustin.examples.serialization;

import static java.lang.System.out;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

/**
 * Simple serialization/deserialization demonstrator.
 * 
 * @author Dustin
 */
public class SerializationDemonstrator
{
   /**
    * Serialize the provided object to the file of the provided name.
    * @param objectToSerialize Object that is to be serialized to file; it is
    *     best that this object have an individually overridden toString()
    *     implementation as that is used by this method for writing our status.
    * @param fileName Name of file to which object is to be serialized.
    * @throws IllegalArgumentException Thrown if either provided parameter is null.
    */
   public static <T> void serialize(final T objectToSerialize, final String fileName)
   {
      if (fileName == null)
      {
         throw new IllegalArgumentException(
            "Name of file to which to serialize object to cannot be null.");
      }
      if (objectToSerialize == null)
      {
         throw new IllegalArgumentException("Object to be serialized cannot be null.");
      }
      try (FileOutputStream fos = new FileOutputStream(fileName);
           ObjectOutputStream oos = new ObjectOutputStream(fos))
      {
         oos.writeObject(objectToSerialize);
         out.println("Serialization of Object " + objectToSerialize + " completed.");
      }
      catch (IOException ioException)
      {
         ioException.printStackTrace();
      }
   }

   /**
    * Provides an object deserialized from the file indicated by the provided
    * file name.
    * 
    * @param <T> Type of object to be deserialized.
    * @param fileToDeserialize Name of file from which object is to be deserialized.
    * @param classBeingDeserialized Class definition of object to be deserialized
    *    from the file of the provided name/path; it is recommended that this
    *    class define its own toString() implementation as that will be used in
    *    this method's status output.
    * @return Object deserialized from provided filename as an instance of the
    *    provided class; may be null if something goes wrong with deserialization.
    * @throws IllegalArgumentException Thrown if either provided parameter is null.
    */
   public static <T> T deserialize(final String fileToDeserialize, final Class<T> classBeingDeserialized)
   {
      if (fileToDeserialize == null)
      {
         throw new IllegalArgumentException("Cannot deserialize from a null filename.");
      }
      if (classBeingDeserialized == null)
      {
         throw new IllegalArgumentException("Type of class to be deserialized cannot be null.");
      }
      T objectOut = null;
      try (FileInputStream fis = new FileInputStream(fileToDeserialize);
           ObjectInputStream ois = new ObjectInputStream(fis))
      {
         objectOut = (T) ois.readObject();
         out.println("Deserialization of Object " + objectOut + " is completed.");
      }
      catch (IOException | ClassNotFoundException exception)
      {
         exception.printStackTrace();
      }
      return objectOut;
   }
}

下一个代码清单说明了如何使用SerializationDemonstrator类对标准Java字符串(可序列化)进行序列化和反序列化。 屏幕快照显示在代码清单之后,并通过SerializationDemonstrator类的serializedeserialize serialize方法显示了运行String的输出(在NetBeans中)。

在字符串上运行SerializationDemonstrator方法

SerializationDemonstrator.serialize("Inspired by Actual Events", "string.dat");
final String stringOut = SerializationDemonstrator.deserialize("string.dat", String.class);

outputStringSerializationDemonstrator

接下来的两个代码清单是针对Person.java类及其作为属性类型的一个类( CityState.java )。 尽管Person实现了Serializable ,但CityAndState类没有实现。

人.java

package dustin.examples.serialization;

import java.io.Serializable;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class Person implements Serializable
{
   private String lastName;
   private String firstName;
   private CityState cityAndState;

   public Person(
      final String newLastName, final String newFirstName,
      final CityState newCityAndState)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.cityAndState = newCityAndState;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   @Override
   public String toString()
   {
      return this.firstName + " " + this.lastName + " of " + this.cityAndState;
   }
}

CityAndState.java

package dustin.examples.serialization;

/**
 * Simple class storing city and state names that is NOT Serializable.
 * 
 * @author Dustin
 */
public class CityState
{
   private final String cityName;
   private final String stateName;

   public CityState(final String newCityName, final String newStateName)
   {
      this.cityName = newCityName;
      this.stateName = newStateName;
   }

   public String getCityName()
   {
      return this.cityName;
   }

   public String getStateName()
   {
      return this.stateName;
   }

   @Override
   public String toString()
   {
      return this.cityName + ", " + this.stateName;
   }
}

下一个代码清单演示如何在具有不可序列化的CityState的可序列化Person类上运行SerializationDemonstrator 。 代码清单后是NetBeans中输出的屏幕快照。

在具有不可序列化的CityState的可序列化人员上运行SerializationDemonstrator方法

final Person personIn = new Person("Flintstone", "Fred", new CityState("Bedrock", "Cobblestone"));
SerializationDemonstrator.serialize(personIn, "person.dat");

final Person personOut = SerializationDemonstrator.deserialize("person.dat", Person.class);

serializationDemonstratorOnSerializablePersonNonSerializableCityState

在这种情况下, CityState类是我自己的类,可以将其设置为Serializable。 但是,假设该类是第三方框架或库的一部分,并且我无法更改该类本身,则可以将Person更改为使用自定义序列化和反序列化,并正确使用CityState 。 这在适用于Person的类SerializablePerson的下一个代码清单中显示。

SerializablePerson.java

package dustin.examples.serialization;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class SerializablePerson implements Serializable
{
   private String lastName;
   private String firstName;
   private CityState cityAndState;

   public SerializablePerson(
      final String newLastName, final String newFirstName,
      final CityState newCityAndState)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.cityAndState = newCityAndState;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   @Override
   public String toString()
   {
      return this.firstName + " " + this.lastName + " of " + this.cityAndState;
   }

   /**
    * Serialize this instance.
    * 
    * @param out Target to which this instance is written.
    * @throws IOException Thrown if exception occurs during serialization.
    */
   private void writeObject(final ObjectOutputStream out) throws IOException
   {
      out.writeUTF(this.lastName);
      out.writeUTF(this.firstName);
      out.writeUTF(this.cityAndState.getCityName());
      out.writeUTF(this.cityAndState.getStateName());
   }
 
   /**
    * Deserialize this instance from input stream.
    * 
    * @param in Input Stream from which this instance is to be deserialized.
    * @throws IOException Thrown if error occurs in deserialization.
    * @throws ClassNotFoundException Thrown if expected class is not found.
    */
   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
   {
      this.lastName = in.readUTF();
      this.firstName = in.readUTF();
      this.cityAndState = new CityState(in.readUTF(), in.readUTF());
   }

   private void readObjectNoData() throws ObjectStreamException
   {
      throw new InvalidObjectException("Stream data required");
   }
}

上面的代码清单显示SerializablePerson具有自定义的writeObject和readObject方法,以支持自定义的序列化/反序列化, CityState适当地处理其CityState序列化类型CityState属性。 接下来显示用于通过SerializationDemonstrator运行该类的代码片段以及这样做的成功输出。

在SerializablePerson上运行SerializationDemonstrator

final SerializablePerson personIn = new SerializablePerson("Flintstone", "Fred", new CityState("Bedrock", "Cobblestone"));
SerializationDemonstrator.serialize(personIn, "person.dat");

final SerializablePerson personOut = SerializationDemonstrator.deserialize("person.dat", SerializablePerson.class);

serializationDemonstratorOnSerializablePersonPlusNonSerializableCityState

上面描述的方法将允许将不可序列化的类型用作可序列化类的属性,而无需使那些字段处于瞬态状态。 但是,如果前面显示的CityState实例需要在多个可序列化的类中使用,则最好使用可序列化的装饰器装饰CityState类,然后在需要序列化的类中使用该序列化的装饰器类。 下一个代码清单显示SerializableCityState ,它用序列化的版本装饰CityState

SerializableCityState

package dustin.examples.serialization;

import java.io.IOException;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;

/**
 * Simple class storing city and state names that IS Serializable. This class
 * decorates the non-Serializable CityState class and adds Serializability.
 * 
 * @author Dustin
 */
public class SerializableCityState implements Serializable
{
   private CityState cityState;

   public SerializableCityState(final String newCityName, final String newStateName)
   {
      this.cityState = new CityState(newCityName, newStateName);
   }

   public String getCityName()
   {
      return this.cityState.getCityName();
   }

   public String getStateName()
   {
      return this.cityState.getStateName();
   }

   @Override
   public String toString()
   {
      return this.cityState.toString();
   }

   /**
    * Serialize this instance.
    * 
    * @param out Target to which this instance is written.
    * @throws IOException Thrown if exception occurs during serialization.
    */
   private void writeObject(final ObjectOutputStream out) throws IOException
   {
      out.writeUTF(this.cityState.getCityName());
      out.writeUTF(this.cityState.getStateName());
   }
 
   /**
    * Deserialize this instance from input stream.
    * 
    * @param in Input Stream from which this instance is to be deserialized.
    * @throws IOException Thrown if error occurs in deserialization.
    * @throws ClassNotFoundException Thrown if expected class is not found.
    */
   private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException
   {
      this.cityState = new CityState(in.readUTF(), in.readUTF());
   }

   private void readObjectNoData() throws ObjectStreamException
   {
      throw new InvalidObjectException("Stream data required");
   }
}

可以直接在Person类中使用此可序列化的修饰器,并且封闭的Person可以使用默认序列化,因为其字段都是可序列化的。 这显示在Person改编的Person2的下一个代码清单中。

Person2.java

package dustin.examples.serialization;

import java.io.Serializable;

/**
 * Person class.
 * 
 * @author Dustin
 */
public class Person2 implements Serializable
{
   private final String lastName;
   private final String firstName;
   private final SerializableCityState cityAndState;

   public Person2(
      final String newLastName, final String newFirstName,
      final SerializableCityState newCityAndState)
   {
      this.lastName = newLastName;
      this.firstName = newFirstName;
      this.cityAndState = newCityAndState;
   }

   public String getFirstName()
   {
      return this.firstName;
   }

   public String getLastName()
   {
      return this.lastName;
   }

   @Override
   public String toString()
   {
      return this.firstName + " " + this.lastName + " of " + this.cityAndState;
   }
}

可以像下面的代码清单中所示那样执行该代码,然后在NetBeans输出窗口中看到其输出。

针对Person2 / SerializableCityState运行SerializationDemonstrator

final Person2 personIn = new Person2("Flintstone", "Fred", new SerializableCityState("Bedrock", "Cobblestone"));
SerializationDemonstrator.serialize(personIn, "person.dat");

final Person2 personOut = SerializationDemonstrator.deserialize("person.dat", Person2.class);

serializedOutputDemoPerson2SerializedCityState

自定义序列化可用于允许对具有不可序列化类型的属性的类进行序列化,而不会使那些不可序列化类型的属性瞬变。 当可序列化的类需要使用不可序列化且无法更改的类型的属性时,这是一种有用的技术。


翻译自: https://www.javacodegeeks.com/2014/02/serializing-java-objects-with-non-serializable-attributes.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值