Java序列化

来源:互联网 发布:信息技术教学的软件 编辑:程序博客网 时间:2024/06/16 01:54

      对象序列化的目标是将对象保存到磁盘中,或允许在网络中直接传输对象。序列化机制允许把内存中的Java对象转换为平台无关的二进制流,从而允许把这种二进制流保存在磁盘上,或通过网络进行传输。其他程序获取到二进制流就能将其恢复成原来的Java对象。

类可序列化必须实现如下两接口之一:

Serializable

Externalizable

实现Serializable接口

  • writeObject()负责序列化,默认调用out.defaultWriteObject保存Java对象各实例变量。
  • readObject()负责反序列化,默认调用in.defaultReadObject来恢复Java对象的非瞬态实例变量。
  • readObjectNoData()用来正确初始化反序列化对象,如版本不同,流被篡改等。
  • writeReplace()writeObject()之前被调用,将对象序列化成其他对象。
  • readResolve()readObject()之后被调用,实现保护性复制整个对象,该方法返回值将代替原来反序列化的对象。所有单例类、枚举类在实现序列化时都应该提供此方法,这样才可以保证反序列化的对象依然正常。对于final类重新此方法不会有任何问题;否则,重新此方法应尽量使用private修饰。

实现Externalizable接口

实现该接口需要强制自定义序列化。

  • readExternal(ObjectInput in):负责反序列化。
  • writeExternal(ObjectOutput out):负责序列化。

对比


注意:对象的类名、实例变量(基本类型、数组及对其他对象的引用)都会被序列化;方法、类变量、transient实例变量都不会被序列化。

反序列化

      反序列化读取的仅仅是Java对象的数据,而不是Java类,所以采用反序列化恢复Java对象时必须提供该Java对象所属类的class文件。反序列化无须通过构造器来初始化Java对象。

序列化算法

①所有保存在磁盘中的对象都有一个序列化编号;

②当程序试图序列化一个对象时,程序必须先检查该对象是否已经被序列化过:

   未序列化,将对象转换成字节序列并输出;

   已序列化,直接输出一个序列化编号,不再重新序列化该对象。

      这样就存在一个问题,当程序序列化一个可变对象时,只有第一次调用writeObject()方法才会将对象转换为字节序列输出,再次调用writeObject()方法将只输出前面的序列化编号,即使实例变量已经修改,修改的值也不会被输出。

版本

      由上可知,序列化对象时必须提供该对象的class文件,问题是,随着项目升级,class文件也会升级,如何保证两个class文件的兼容性?

      Java序列化机制为序列化类提供了一个private static finalserialVersionUID,用于标识序列化版本。一个类升级后,只要该值不变,则被认为是同一个序列化版本。

      为保证反序列化时版本的兼容性与移植性,最好自己定义serialVersionUID值。如果不显式定义此值,则JVM根据类相关信息进行计算。

      如果修改了类的非瞬态实例变量,则可能导致序列化版本不兼容:

①对象流中的对象和新类中包含同名的实例变量,但实例类型不同,则序列化失败,类定义应该更新serialVersionUID值;

②对象流中对象比新类中包含更多实例变量,多出的实例变量被忽略,版本可兼容,无须更新serialVersionUID值;

③新类比对象流中的对象包含更多实例变量,版本可兼容,可不更新serialVersionUID,但反序列化得到的新对象中多出的实例变量都是null0

 

 

 

原创粉丝点击