java对象转换为字节序列,序列化。
字节序列恢复为java对象,反序列化。
java.io.ObjectOutputStream 代表输出流,它的writeObject(Object obj)对指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流。
java.io.ObjectInputStream代表输入流,它的readObject()方法从一个源输入流中读取字节,再把它们反序列化为一个对象。
只有实现了Serializable和Externalizable接口的类对象才能被序列化,否则ObjectOutputStream的writeObject(Object obj)将抛出IOException。
Externalizable继承了Serializable接口,实现了Externalizable完全由自身来控制序列化的行为,而仅仅实现了Serializable的类采用默认的方式进行
序列化。
如果按照默认方式序列化,如果一个ObjectOutputStream对象多次序列化同一对象,那么由一个ObjectInputStream对象对象反序列化的也是同一个对象。
如果按照ObjectOutputStream的默认方式序列化,静态变量和transient变量都没有被序列化,当ObjectInputStream按照默认方式反序列化的时候,有
如下特点:
1.如果内存中对象所属的类还没有被加载,那么先加载并初始化这个类。如果classpath中不存在相应的文件,那么会抛出ClassNotFoundException。
2.反序列化不会调用类的任何构造方法。
序列化对象图
如果一个对象Customer有一个name和Set<Order>属性,那么序列化的时候,会把name和set都序列化,抽象地讲,将序列化直接引用和间接引用的对象。
控制序列化行为
方法:
private void writeObject(java.io.ObjectOutputStream out)throws IOException;
private void readObject(java.io.ObjectInputStream in)throws IOException,ClassNotFoundException;
如果序列化一个对象,如果该对象有writeObject()方法的时候,就执行这个方法,否则执行默认序列化方法。
在这个对象的writeObject方法中,可以先调用ObjectOutputStream的defaultWriteObject()方法,使得输出流先执行默认的序列化操作。
如果反序列化一个对象,如果该对象有readObject()方法的时候,就执行这个方法,否则执行默认序列化方法。
在这个对象的readObject方法中,可以先调用ObjectInputStream的defaultReadObject()方法,使得输出流先执行默认的反序列化操作。
值得注意的是,以上的readObject()方法和writeObject()方法并不是在java.io.Serializable接口中定义的。
当一个软件系统想要拓展第三方java类库(JDK类库)的功能时,最常见的方式是实现第三方类库的接口,或者创建类库中抽象方法的子类。但
以上writeObject()和readObject()方法并不是在java.io.Serializable接口中定义。JDK类库的设计人员并没有把这两个方法放在Serializable中,
这样做的优点在于:
1.不必公开这两个方法的访问权限,以便封装序列化的细节。如果这两个方法放在Serializable中,就必须定义为public类型。
2.不必强迫用户定义的可序列化类实现这两个方法。如果把这两个方法放在Serializable接口中,它的实现类就必须实现这些
方法,否则就只能声明为抽象类。
以下情况,可以考虑采用用户自定义的序列化方式,从而控制序列化的行为:
1.确保序列化的安全性,对敏感的信息进行加密后再序列化,在反序列化的时候需要解密。
2.确保对象的成员变量符合正确的约束条件。
3.优化序列化的性能。
4.便于更好地封装类的内部数据,确保类的接口不会被类的内部实现所束缚。
Serializable接口单例
序列化单例,反序列化后就存在不止一个单例对象,为此,要增加方法
private Object readResolve() throws ObjectStreamException;
与此相对应的方法有
private Object writeReplace()。
实现externalizable接口
public void writeExternal(ObjectOutput out) throws IOException;
public void readExternal(ObjectInput in)throws IOException,ClassNotFoundException;
writeExternal()方法负责序列化操作,readExternal()负责反序列化操作。
实现了Externalizable接口的类的对象进行反序列化时,会先调用类的不带参数的构造函数,这个和默认的反序列化方式不同。
序列化与版本
默认的serialVersionUID,不同编译器,可能相同,也可能不同。
显示的serialVersionUID
在某些场合,希望类的不同版本对序列化兼容,因而需要确保不同的版本需要有相同的serialVersionUID.
在某些场合,不希望不同版本对序列化兼容,因而需要确保类的不同版本需要有不同的serialVersionUID.
用serialVersionUID来控制序列化兼容的能力是有限的。当一个类的不同版本的serialVersionUID相同,仍然可能出现
序列化不兼容的情况。因为序列化兼容不仅取决于serialVersionUID,还取决于类的不同版本的实现细节和序列化细节。