序列化一个拥有非序列化属性的Java对象
参考JavaCodeGeeks上个这篇文章 http://www.javacodegeeks.com/2014/02/serializing-java-objects-with-non-serializable-attributes.html 作者Dustin Marx
有很多原因,使得我们想使用自定义的序列化方法代替Java默认的序列化方法。最普遍的一个原因就是为了提升性能。其他的原因大都是默认的序列化机制不支持。其中一个非常普遍的场景就是,要序列化一个拥有非序列化属性的对象。
下面的代码展示了一个小例子:
SerializationDemonstrator.java
package com.chengjf;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
public class SerializationDemonstrator {
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.");
}
FileOutputStream fos = null;
ObjectOutputStream oos = null;
try {
fos = new FileOutputStream(fileName);
oos = new ObjectOutputStream(fos);
oos.writeObject(objectToSerialize);
System.out.println("Serialization of object " + objectToSerialize
+ " completed!");
oos.close();
fos.close();
} catch (IOException ioException) {
ioException.printStackTrace();
}
}
public static <T> T deserialize(final String fileToSerialize,
final Class<T> classBeingDeserialized) {
if (fileToSerialize == 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;
FileInputStream fis = null;
ObjectInputStream ois = null;
try {
fis = new FileInputStream(fileToSerialize);
ois = new ObjectInputStream(fis);
objectOut = (T) ois.readObject();
System.out.println("Deserialization of object " + objectOut
+ " is completed!");
ois.close();
fis.close();
} catch (IOException ioException) {
ioException.printStackTrace();
} catch (ClassNotFoundException classNotFoundException) {
classNotFoundException.printStackTrace();
}
return objectOut;
}
}
下面的代码调用了SerializationDemonstrator的方法,对Java的String对象进行了序列化和反序列化。
SerializationDemonstrator.serialize("My name is chengjf.", "name.dat");
String stringOut = SerializationDemonstrator.deserialize("name.dat", String.class);
输出结果如下:
Serialization of object My name is chengjf completed!
Deserialization of object My name is chengjf is completed!
下面拿Person和PersonName举一个拥有非序列化属性的对象的例子:
PersonName.java
package com.chengjf;
public class PersonName {
private String firstName;
private String lastName;
public PersonName(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
@Override
public String toString() {
return this.firstName + " " + this.lastName;
}
}
Person.java
package com.chengjf;
import java.io.Serializable;
public class Person implements Serializable {
private PersonName name;
private int age;
public Person(PersonName name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
}
上述两个Java代码,Person实现了Serializable接口,但是Person里的name属性是PersonName,并没有实现Serializable接口。
我们使用SerializationDemonstrator来进行序列化和反序列化:
Person personIn = new Person(new PersonName("Jianfeng", "Cheng"), 24);
SerializationDemonstrator.serialize(personIn, "person.dat");
Person personOut = SerializationDemonstrator.deserialize("person.dat",
Person.class);
运行结果如下:
java.io.NotSerializableException: com.sharp.PersonName
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at com.sharp.SerializationDemonstrator.serialize(SerializationDemonstrator.java:25)
at com.sharp.Pass.main(Pass.java:11)
java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: com.sharp.PersonName
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.defaultReadFields(Unknown Source)
at java.io.ObjectInputStream.readSerialData(Unknown Source)
at java.io.ObjectInputStream.readOrdinaryObject(Unknown Source)
at java.io.ObjectInputStream.readObject0(Unknown Source)
at java.io.ObjectInputStream.readObject(Unknown Source)
at com.sharp.SerializationDemonstrator.deserialize(SerializationDemonstrator.java:51)
at com.sharp.Pass.main(Pass.java:13)
Caused by: java.io.NotSerializableException: com.sharp.PersonName
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.defaultWriteFields(Unknown Source)
at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
at java.io.ObjectOutputStream.writeObject0(Unknown Source)
at java.io.ObjectOutputStream.writeObject(Unknown Source)
at com.sharp.SerializationDemonstrator.serialize(SerializationDemonstrator.java:25)
at com.sharp.Pass.main(Pass.java:11)
在这个例子中,PersonName类是我写的,我可以让其实现Serializable接口,从而实现序列化的目的。然而,现实中,往往遇到的那些第三方的类库,你无法修改它们的class。但是我可以修改Person类,实现自定义的序列化方法和反序列化方法,来适应PersonName。下面是个例子:
SerializationPerson.java
package com.chengjf;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializablePersion implements Serializable{
private PersonName name;
private int age;
public SerializablePersion(PersonName name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
private void writeObject(final ObjectOutputStream out) throws IOException{
out.writeUTF(this.name.getFirstName());
out.writeUTF(this.name.getLastName());
out.write(this.age);
}
private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException{
this.name = new PersonName(in.readUTF(), in.readUTF());
this.age = in.read();
}
}
使用SerializationDemonstrator进行序列化和反序列化:
SerializablePersion personIn = new SerializablePersion(new PersonName("Jianfeng", "Cheng"), 24);
SerializationDemonstrator.serialize(personIn, "person.dat");
SerializablePersion personOut = SerializationDemonstrator.deserialize("person.dat",
SerializablePersion.class);
结果如下:
Serialization of object Jianfeng Cheng 24 completed!
Deserialization of object Jianfeng Cheng 24 is completed!
上面的方法,SerializablePersion类通过自定义序列化方法和反序列化方法,将非序列化的PersonName类的属性写出和读入,达到了这一目的。但是如果PersonName类也在其他地方需要序列化,那么每次都这么操作非常麻烦。我们可以通过给PersonName类加一个序列化装饰器。代码如下:
SerializablePersonName.java
package com.chengjf;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
public class SerializablePersonName implements Serializable {
private PersonName name;
public SerializablePersonName(String firstName, String lastName) {
this.name = new PersonName(firstName, lastName);
}
public String getFirstName() {
return this.name.getFirstName();
}
public String getLastName() {
return this.name.getLastName();
}
@Override
public String toString() {
return this.name.toString();
}
private void writeObject(ObjectOutputStream out) throws IOException {
out.writeUTF(this.name.getFirstName());
out.writeUTF(this.name.getLastName());
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
this.name = new PersonName(in.readUTF(), in.readUTF());
}
}
修改Person类,将PersonName替换成SerializablePersonName:
Person2.java
package com.chengjf;
import java.io.Serializable;
public class Person2 implements Serializable {
private SerializablePersonName name;
private int age;
public Person2(SerializablePersonName name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
}
使用SerializationDemonstrator序列化和反序列化:
Person2 personIn = new Person2(new SerializablePersonName("Jianfeng", "Chengjf"), 24);
SerializationDemonstrator.serialize(personIn, "person.dat");
Person2 personOut = SerializationDemonstrator.deserialize("person.dat",
Person2.class);
结果如下:
Serialization of object Jianfeng Chengjf 24 completed!
Deserialization of object Jianfeng Chengjf 24 is completed!
自定义序列化方法可以用来序列化和反序列化那些拥有非序列化属性但又无法更改的对象。