深入java 序列化和反序列化基本用法

这篇文章总结一下java序列化和反序列化基础用法,序列化和反序列化注意的一些细节问题。更多的源代码请访问我的github:https://github.com/yangsheng20080808/deepIntoJava

原创不易,转载请声明出处。

本文分为3大部分

  • 我们为什么需要序列化和反序列化
  • java序列化和反序列化基本用法
  • 版本号对于序列化和反序列化的影响

我们为什么需要序列化和反序列化

Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java对象序列化就能够帮助我们实现该功能。
1. 使用Java对象序列化,在保存对象时,会把其状态保存为一组字节,在未来,再将这些字节组装成对象。必须注意地是,对象序列化保存的是对象的”状态”,即对象的成员变量。由此可知,对象序列化不会关注类中的静态变量,也就是由static声明的字段
2. 除了在持久化对象时会用到对象序列化之外,当使用RMI(远程方法调用),或在网络中传递对象时,都会用到对象序列化。Java序列化API为处理对象序列化提供了一个标准机制,该API简单易用。

一句话说白了:我们有一个需要将某个对象保存很久,而且过了一段时间我们还可以将这个对象还原的场景。

java序列化和反序列化基本用法

很经常见到很多的类都实现了Serializable这个接口,这个接口是提供类对象序列化和反序列化的接口,我们直接举例子来吧:
深入java 序列化和反序列化基本用法_第1张图片

运行结果
深入java 序列化和反序列化基本用法_第2张图片

如果我们在序列化完成后,更改了版本(升级为2L:serialVersionUID = 2L),类中其他的属性都没有改变,反序列化会失败:
深入java 序列化和反序列化基本用法_第3张图片

serialVersionUID的作用: serialVersionUID 用来表明类的不同版本间的兼容性。如果你修改了此类, 要修改此值。否则以前用老版本的类序列化的类恢复时会出错。Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来 的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序 列化,否则就会出现序列化版本不一致的异常。

假如我们的版本号没有改变,但是类增加了一些属性,还是可以兼容的:例子
深入java 序列化和反序列化基本用法_第4张图片

直接将我们的对象反序列化:
这里写图片描述
还是可以兼容,因为我反序列化的时候在序列化文件中读取到的两个参数作为构造函数的参数,反序列化还是成功。

假如这个时候我们将参数只有两个(上一个版本的构造函数)的构造函数注释掉:运行就会报错了:
深入java 序列化和反序列化基本用法_第5张图片

所以如果我们希望升级版本后可以兼容原来的版本,第一:不要改动serialVersionUID;第二:不要删除上一个版本的构造函数。

版本号对于序列化和反序列化的影响

设置 serialVersionUID默认的生成方式: private static final long serialVersionUID = 1L;

如果我们不希望通过编译来强制划分软件版本,即实现序列化接口的实体能够兼容先前版本,未作更改的类,就需要显式地定义一个名为serialVersionUID,类型为long的变量,不修改这个变量值的序列化实体都可以相互进行串行化和反串行化。

为了在反序列化时,确保类版本的兼容性,最好在每个要序列化的类中加入 private static final long serialVersionUID这个属性,而且同时具体数值自己定义。这样,即使某个类在与之对应的对象 已经序列化出去后做了修改,该对象依然可以被正确反序列化,可以向下兼容。

否则,如果不显式定义该属性,这个属性值将由JVM根据类的相关信息计算,而修改后的类的计算 结果与修改前的类的计算结果往往不同,从而造成对象的反序列化因为类版本不兼容而失败。

不显式定义这个属性值的另一个坏处是,不利于程序在不同的JVM之间的移植。因为不同的编译器实现该属性值的计算策略可能不同,从而造成虽然类没有改变,但是因为JVM不同,可能出现因类版本不兼容而无法正确反序列化的现象出现。

你可能感兴趣的:(java深入理解,java)