什么是序列化
(1)序列化是将对象转变为字节序列的过程,反序列化则是将字节序列恢复为对象的过程。
(2)对象序列化保存的是对象的状态,即它的成员变量;
(3)对象的持久化存储(写文件),网络传输对象,或者使用RMI都会用到对象序列化。
JAVA 提供的操作序列化的接口
(1)Java 主要提供给了两个接口实现对象的序列化和反序列化,java.io.ObjectInputStream的readObject()方法 和 java.io.ObjectOutputStream 的writeObject(Object obj)方法;
(2)只有实现Serializable或Externalizable接口的类的对象才能被序列化;否则会抛出java.io.NotSerializableException异常。
JAVA对象序列化示例
(1)类实现 Serializable接口
类中未定义 writeObject(Object obj)和readObject方法,那么按照默认的序列化方式实现序列化和反序列化。
以上代码展示了如何序列化对象到一个文件中并从文件中反序列化的过程。
序列化的过程:
首先创建 ObjectOutputStream 对象,该对象可以包装其他输出流,比如文件输出流;
调用对象输出流的writeObject(Object obj)方法,可以将对象写入到输出流中。
关闭流。结束。
对象持久化到文件中的过程结束。
反序列化的过程:
首先创建ObjectInputStream对象,类似于ObjectOutputStream;
调用对象输入流的readObject()方法,读对象到输入流中。返回字节序列转化的对象。
关闭流;结束。
输出:
类中定义了 writeObject(Object obj)和readObject方法,那么按照自定义的序列化方实现式序列化和反序列化。
在Student.java添加如下两个方法:
输出结果:
(2)transient 关键字
当某个成员变量声明为transient后,默认的序列化机制就会忽略该变量。
将age字段声明为transient,
输出 age=0:
此时我们可以选择单独传输某个字段;修改writeObject和readObject方法:
结果:
单独传输了age 字段,因此age=18;
补充:除了上面提到的两个方法外:
private void writeObject(java.io.ObjectOutputStream out) throws IOException ;
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
还有其他三个方法,可供我们定制自己的序列化反序列化过程:
private void readObjectNoData() throws ObjectStreamException;
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
readObjectNoData() :用于初始化反序列化对象,当发生一些情况导致反序列化对象不能获得数据时调用;
writeReplace() :指派其他对象写入序列化的流中;
readResolve():返回的对象替换反序列化创建的实例;
readResolve() 常用于单例模式中;示例:
修改Student.java,添加instanceHoder:
修改SimpleSerial.java:
结果输出:
可以看到,s==student返回false,也就是说反序列化后得到的Student对象并不是唯一的instance,因此这样写单例模式是失败的;
修正:
再次运行:
总结:
当进行序列化的时候:
首先JVM会先调用writeReplace方法,在这个阶段,我们可以进行张冠李戴,将需要进行序列化的对象换成我们指定的对象.
跟着JVM将调用writeObject方法,来将对象中的属性一个个进行序列化,我们可以在这个方法中控制住哪些属性需要序列化.
当反序列化的时候:
JVM会调用readObject方法,将我们刚刚在writeObject方法序列化好的属性,反序列化回来.
然后在readResolve方法中,我们也可以指定JVM返回我们特定的对象(不是刚刚序列化回来的对象).
注意到在writeReplace和readResolve,我们可以严格控制singleton的对象,在同一个JVM中完完全全只有唯一的对象,控制不让singleton对象产生副本.
(3)类实现Externalizable 接口
Externalizable 接口继承 Serializable接口:
public interface Externalizable extends Serializable ;
Serializable接口是一个mark interface,没有实际方法;而Externalizable 接口提供了两个方法:
void readExternal (ObjectInput in) ;
void writeExternal (ObjectOutput out) ;
readExternal (ObjectInput in):从输入流中读取内容恢复对象;
writeExternal (ObjectOutput out) : 写入对象到输出流中;
示例:
使用Externalizable进行序列化时,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。因此,必须提供一个无参构造器,访问权限为public;否则会抛出java.io.InvalidClassException 异常;
总结:Externalizable接口实现的功能与Serializable接口类似,Serializable序列化时不会调用默认的构造器,而Externalizable序列化时会调用默认构造器;