Two of the byte streams, ObjectInputStream and ObjectOutputStream, are specialized streams that let you read and write objects. Reading and writing objects is a process known as object serialization.
编写序列化的类
仅当一个类实现序列化接口的时候它的对象才是可序列化的。因此,如果要实现一个类的对象的序列化,该类必须实现序列化接口。在Java中只要我们的类实现了java.io.Serializable接口,就可以利用ObjectOutputStream的writeObject()方法将一个对象序列化;利用ObjectInputStream的readObject()方法,可以返回读出的object对象。
Serializable接口是一个空的接口,它不包括任何方法的定义,不需要我们实现任何方法,它的目的只不过是声明了该类的对象是可序列化的。
简单的序列化类如下:
imports java.io.*;
public class SerializeClass implements Serializable {
int company_id;
String company_name;
String company_addr;
public SerializeClass(int company_id,String company_name,String company_addr) {
this.company_id = company_id;
this.company_name = company_name;
this.company_addr = company_addr;
}
public String toString() {
return "Company ID:" + company_id + " Company Name:"+ company_name +" Company Address:" + company_addr;
}
public void serialize(ObjectOutputStream s) throws Exception {
s.writeObject(this);
}
public static SerializeClass deserialize(ObjectInputStream s) throws Exception,ClassNotFoundException {
return (SerializeClass) s.readObject() ;
}
}
/*测试*/
public void testSerialize() throws Exception {
String filename = "bin/serializeclass.dat";
FileOutputStream out = null;
ObjectOutputStream oout = null;
out = new FileOutputStream(filename);
oout = new ObjectOutputStream(out);
SerializeClass ser = new SerializeClass(578,"3C","ShenZhen");
ser.serialize(oout);
oout.close();
out.close();
FileInputStream in = null;
ObjectInputStream oin = null;
in = new FileInputStream(filename);
oin = new ObjectInputStream(in);
SerializeClass desr = SerializeClass.deserialize(oin);
System.out.println(desr);
oin.close();
in.close();
}
问题
1) 实现了Serializable接口的类的改动变得很难(兼容性问题)。
如果你想保持向下兼容性的话,这意味着要求新版本反序列化老版本序列化的数据流;如果你想保持向上兼容性的话,这意味着要求老版本反序列化新版本的数据流。
尤其是向下兼容性的问题带来了让我们的代码实现很难改动的问题,为了能在新版本中反序列化老版本的数据流,类的实现必须要保留老版本的实现过程。这无疑是我们给自己加带上的一副枷锁。
2) 实现了Serializable接口的类的测试成本大大增加了可以想象,当新版本发布时,为了保持兼容性,你的测试量将是版本数的平方!
3) 草率的接受默认的序列化方式可能会带来性能问题,甚至更糟。
4) 序列化作为额外的一种“构造函数”可能破坏数据的完整性、约束性,甚至,一个精心伪造的数据流所序列化出的对象可能带来安全隐患。
当一个父类实现Serializable接口后,他的子类都将自动的实现序列化。
1. 当序列化遇到继承时引发的问题?
序列化类的所有子类本身都是可序列化的。但是,如果父类是非序列化的,要求子类是可序列化的,该怎么办呢?
"To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime. "(允许非序列化类的子类型序列化,仅当该类的扩展子类有一个可访问的无参数的构造函数用来初始化该类的状态时,其子类可以负责保存和恢复父类型的公有的、保护的和(如果可访问)包的域的状态。否则的话声明其子类是可序列化的将会发生错误,该错误在运行的时候被检测)
也就是说,要为一个没有实现Serializable接口的父类,编写一个能够序列化的子类要做两件事情:
其一,父类要有一个无参的constructor。
public class ParentClass {
int parentValue;
public ParentClass(); //如果要求其子类可以序列化,该无参数构造函数是必须的
public ParentClass(int parentValue) {
this.parentValue = parentValue;
}
public String toString() {
return "Parent Value:" + parentValue;
}
}
其二,子类要负责序列化(反序列化)父类的域。
子类序列化父类域可以通过自定义序列化实现。自定义序列化要求类中必须有writeObject和readObject方法,它们的定义必须是如下形式,writeObject方法中必须首先调用ObjectOutputStream类的defaultWriteObject方法处理默认的序列化,其它的序列化在其后进行处理;readObject方法读取的顺序必须和写入的顺序一样,即首先通过objectInputStream的defaultReadObject方法读取默认的序列化,再依次读出其它的自定义序列化信息。
private void writeObject(ObjectOutputStream s) throws IOException {
s.defaultWriteObject();
// customized serialization code
}
private void readObject(ObjectInputStream s) throws IOException {
s.defaultReadObject();
// customized deserialization code
// followed by code to update the object, if necessary
}
如果可序列化类中包含正确的writeObject/readObject函数对,进行序列化和反序列化的时候,writeObject/readObject就会被调用,以代替默认的序列化行为
public class ChildClass Extends ParantClass implements Serializable {
int subvalue;
public ChildClass(int supervalue,int subvalue) {
super(supervalue);
this.subvalue=subvalue;
}
public String toString() {
return super.toString()+" sub: "+subvalue;
}
private void writeObject(java.io.ObjectOutputStream out) throws IOException{
out.defaultWriteObject();//先序列化对象
out.writeInt(supervalue);//再序列化父类的域
}
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException{
in.defaultReadObject();//先反序列化对象
supervalue=in.readInt();//再反序列化父类的域
}
}
2. 何时接受默认的java序列化?
java默认的序列化行为,java将一切关于对象的信息都保存了下了,也就是说,有些时候那些不需要保存的也被保存了下来。一般情况下,我们仅仅需要保存逻辑数据就可以了。不需要保存的数据我们可以用关键字transient标出。
以下是一个例子:
import java.io.*;
public class Serial implements Serializable {
int company_id;
String company_addr;
transient boolean company_flag;
}
则company_flag字段将不会参与序列化与反序列化,但同时也增加了为它初始值的责任。这也是序列化常常导致的问题之一。因为序列化相当于一个只接受数据流的public构造函数,这种对象构造方法是语言之外的。但他仍然是一种形式上的构造函数。如若你的类不能够通过其他方面来保证初始化,则你需要额外的提供readObject方法,首先正常的反序列化,然后对transient标示的字段进行初始化。
3. 兼容性问题
4. 数据一致性问题与数据约束问题
5. 安全性问题