Java Serializable接口(序列化)理解及自定义序列化
Posted 程广林
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java Serializable接口(序列化)理解及自定义序列化相关的知识,希望对你有一定的参考价值。
1 Serializable接口
(1)简单地说,就是可以将一个对象(标志对象的类型)及其状态转换为字节码,保存起来(可以保存在数据库,内存,文件等),然后可以在适当的时候再将其状态恢复(也就是反序列化)。serialization 不但可以在本机做,而且可以经由网络操作。它自动屏蔽了操作系统的差异,字节顺序等。比如,在 Windows 平台生成一个对象并序列化之,然后通过网络传到一台 Unix 机器上,然后可以在这台Unix机器上正确地重构(deserialization)这个对象。 不必关心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。
另外,还应明白以下几点:
a. java.io.Serializable接口没有任何方法属性域,实现它的类只是从语义上表明自己是可以序列化的。
b. 在对一个 Serializable(可序列化)对象进行重新装配的过程中,不会调用任何构建器(甚至默认构建器)。整个对象都是通过从 InputStream 中取得数据恢复的。
c. 如是要一个类是可序列化的,那么它的子类也是可序列化的。
(2)serialVersionUID
serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的serialVersionUID,也有可能相同。为了提高哦啊serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值。显式地定义serialVersionUID有两种用途:
a. 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
b. 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。
2 自定义序列化:
自定义序列化是由ObjectInput/OutputStream在序列化/反序列化时候通过反射检查该类是否存在以下方法(0个或多个):执行顺序从上往下,序列化调用1和2,反序列调用3和4;transient关键字当某个字段被声明为transient后,默认序列化机制就会忽略该字段。
1Object writeReplace() throws ObjectStreamException;可以通过此方法修改序列化的对象
2void writeObject(java.io.ObjectOutputStream out) throws IOException; 方法中调用defaultWriteObject() 使用writeObject的默认的序列化方式,除此之外可以加上一些其他的操作,如添加额外的序列化对象到输出:out.writeObject("XX")
3void readObject(java.io.ObjectInputStream in) throws Exception; 方法中调用defaultReadObject()使用readObject默认的反序列化方式,除此之外可以加上一些其他的操作,如读入额外的序列化对象到输入:in.readObject()
4Object readResolve() throws ObjectStreamException;可以通过此方法修改返回的对象
例子:单例模式的类实现序列化接口,若使用默认的序列化策略,则在反序列化返回的对象不符合单利模式(反射创建了新的对象,如下PersonSington对象),可以通过修改序列化的readResolve来实现自定义序列化返回结果来实现单例对象唯一(相当于1,2,3方法对4的结果毫无作用)。
public class PersonSinglton implements Serializable { private static final long serialVersionUID = 1L; private String name; private PersonSinglton(String name) { this.name = name; }; private static PersonSinglton person = null; public static synchronized PersonSinglton getInstance() { if (person == null) return person = new PersonSinglton("cgl"); return person; } private Object writeReplace() throws ObjectStreamException { System.out.println("1 write replace start"); return this;//可修改为其他对象 } private void writeObject(java.io.ObjectOutputStream out) throws IOException { System.out.println("2 write object start"); out.defaultWriteObject();
//out.writeInt(1); } private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { System.out.println("3 read object start"); in.defaultReadObject();
//int i=in.readInt(); } private Object readResolve() throws ObjectStreamException { System.out.println("4 read resolve start"); return PersonSinglton.getInstance();//不管序列化的操作是什么,返回的都是本地的单例对象 } public static void main(String[] args) throws Exception { FileOutputStream out = new FileOutputStream(new File("D://person.dat")); ObjectOutputStream op = new ObjectOutputStream(out); op.writeObject(PersonSinglton.getInstance()); op.close(); FileInputStream in = new FileInputStream(new File("D://person.dat")); ObjectInputStream oi = new ObjectInputStream(in); Object person = oi.readObject(); in = new FileInputStream(new File("D://person.dat")); oi = new ObjectInputStream(in); PersonSinglton person1 = (PersonSinglton) oi.readObject(); System.out.println("sington person hashcode:" + person.hashCode()); System.out.println("sington person1 hashcode:" + person1.hashCode()); System.out.println("sington getInstance hashcode:" + PersonSinglton.getInstance().hashCode()); System.out.println("sington person equals:" + (person == PersonSinglton.getInstance())); System.out.println("person equals1:" + (person1 == person)); } }
运行结果:
1 write replace start
2 write object start
3 read object start
4 read resolve start
3 read object start
4 read resolve start
sington person hashcode:1550089733
sington person1 hashcode:1550089733
sington getInstance hashcode:1550089733
sington person equals:true
person equals1:true
3 Externalizable接口
Externalizable继承于Serializable,当使用该接口时,序列化的细节需要由程序员去完成。若writeExternal()与readExternal()方法未作任何处理,那么该序列化行为将不会保存/读取任何一个字段。出结果中所有字段的值均为空。
另外,若使用Externalizable进行序列化,当读取对象时,会调用被序列化类的无参构造器去创建一个新的对象,然后再将被保存对象的字段的值分别填充到新对象中。由于这个原因,实现Externalizable接口的类必须要提供一个无参的构造器,且它的访问权限为public。
以上是关于Java Serializable接口(序列化)理解及自定义序列化的主要内容,如果未能解决你的问题,请参考以下文章
Java序列化为什么必须实现 Serializable 接口???
Java序列化和反序列化为什么要实现Serializable接口