java序列化和反序列化
1、什么是序列化?
序列化就是把对象改成二进制的过程,可以保存到磁盘活着网络发送。
所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现Serializeable接口。
2、序列化实现的方式?
1)实现Serializable接口
序列化步骤:
步骤一:创建一个ObjectOutputStream输出流;
步骤二:调用ObjectOutputStream对象的writeObject输出可序列化对象。
反序列化步骤:
步骤一:创建一个ObjectInputStream输入流;
步骤二:调用ObjectInputStream对象的readObject()得到序列化的对象。
Java序列化同一对象,并不会将此对象序列化多次得到多个对象。
2)实现Externalizable接口
Externalizable:强制自定义序列化
通过实现Externalizable接口,必须实现writeExternal、readExternal方法。
Externalizable接口不同于Serializable接口,实现此接口必须实现接口中的两个方法实现自定义序列化,这是强制性的;特别之处是必须提供pulic的无参构造器,因为在反序列化的时候需要反射创建对象。
3、使用transient关键字选择不需要序列化的字段。
使用transient修饰的属性,java序列化时,会忽略掉此字段,所以反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。
4、静态变量不会被序列化。因为静态变量在全局区,本来流里面就没有写入静态变量。
5、序列化版本号serialVersionUID
我们知道,反序列化必须拥有class文件,但随着项目的升级,class文件也会升级,序列化怎么保证升级前后的兼容性呢?
java序列化提供了一个private static final long serialVersionUID 的序列化版本号,只有版本号相同,即使更改了序列化属性,对象也可以正确被反序列化回来。
如果反序列化使用的class的版本号与序列化时使用的不一致,反序列化会报InvalidClassException异常。
序列化版本号可自由指定,如果不指定,JVM会根据类信息自己计算一个版本号,这样随着class的升级,就无法正确反序列化;不指定版本号另一个明显隐患是,不利于jvm间的移植,可能class文件没有更改,但不同jvm可能计算的规则不一样,这样也会导致无法反序列化。
什么情况下需要修改serialVersionUID呢?分三种情况。
如果只是修改了方法,反序列化不容影响,则无需修改版本号;
如果只是修改了静态变量,瞬态变量(transient修饰的变量),反序列化不受影响,无需修改版本号;
如果修改了非瞬态变量,则可能导致反序列化失败。如果新类中实例变量的类型与序列化时类的类型不一致,则会反序列化失败,这时候需要更改serialVersionUID。如果只是新增了实例变量,则反序列化回来新增的是默认值;如果减少了实例变量,反序列化时会忽略掉减少的实例变量。
6、总结
所有需要网络传输的对象都需要实现序列化接口,通过建议所有的javaBean都实现Serializable接口。
对象的类名、实例变量(包括基本类型,数组,对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。
如果想让某个变量不被序列化,使用transient修饰。
序列化对象的引用类型成员变量,也必须是可序列化的,否则,会报错。
反序列化时必须有序列化对象的class文件。
当通过文件、网络来读取序列化后的对象时,必须按照实际写入的顺序读取。
单例类序列化,需要重写readResolve()方法;否则会破坏单例原则。
同一对象序列化多次,只有第一次序列化为二进制流,以后都只是保存序列化编号,不会重复序列化。
建议所有可序列化的类加上serialVersionUID 版本号,方便项目升级。
转载:
1、https://www.cnblogs.com/9dragon/p/10901448.html#h
2、https://baijiahao.baidu.com/s?id=1636492159314232573&wfr=spider&for=pc

浙公网安备 33010602011771号