0%

Java筑基-:(06)Java基础进阶-序列化

一、序列化与二进制串

序列化其实就是将数据结构转换成二进制串的过程。这里的二进制串,在 Java 中很容易和 String 的概念混淆,实际上 String 也是一种特殊对象(Object),对于跨语言间的通信,序列化后的数据当然不能使某种语言的特殊数据类型。

二进制串在 Java 里面所指的应当是 byte[]

二、序列化/反序列化的目的

  • 数据的生命周期需要比 JVM 长。Java 允许我们在内存中创建可复用的对象,但是一般只有 JVM 存在的时候,这些对象才能存在,即对象生命周期不能比 JVM 更长。所以,如果需要停止 JVM 后数据还存在,就要序列化保存这些数据。

  • 序列化只是对变量而言。序列化对象的时候,只针对变量进行序列化,不针对方法进行序列化。

  • 需要永久保存或者在网络上传输的时候,需要序列化之后才能进行。

三、序列化常见的方案

  • Java 中有 Serializable

  • Android 中有 Parcelable

  • 还有 Json、xml 、Protocol Buffer 等

四、选择合理的序列化方案

  • 性能

  • 通用性

  • 鲁棒性

  • 可调式性/可读性

  • 可扩展/兼容性

  • 安全性/访问限制

五、Serializable

首先, Serializable 只是一个接口,为什么一个空的接口能够实现序列化?因为它只是一个标识、标记!

如果要保存到磁盘上,还需要使用 IO 流(ObjectInputStream/ObjectOutPutStream)来辅助,这个好理解。

六、Externalizable

要自己实现 writeExternal 和 readExternal 方法,但是使用的时候需要注意以下几点:

  • write 和 read 方法二者要对应,比如,write 的阶段没有只写入了一个 String 类型,但是读的时候 读了 String 还要读 int ,这时候就会导致崩溃报错。

  • write 和 read 的顺序要保持一致,比如先 write 了String ,再 write 了int ,读的时候如果先读int ,后读String ,也会导致报错

  • 要有默认无参的、public类型的构造函数,如果你自己写了有参数的构造函数,一定再加上无参的默认构造函数

为什么要有一个 public 无参的构造函数?

七、相关面试题

7.1 什么是 serialVersionUID ? 如果不定义会发生什么?

  1. serialVersionUID 是一个 private static final long 类型的 ID ,它通常是对象的 hashCode

  2. 它用于对象的版本控制,我们可以在类文件中指定这个值

  3. 如果不指定它的值,当你序列化类 A.java 生成了文件 a.txt ,之后,在 A 类中添加了一个字段 sex ,则在反序列化的时候会报错,提示版本不一致

  4. 还有,如果指定serialVersionUID 为 1,序列化 A 的时候生成了 a.txt ,这时候添加了字段 sex ,如果此时不改 serialVersionUID 的值,反序列化的时候,能成功,只是 sex 字段为 null(这里说的是对象类型,如果是 int 等类型就会是 0 ,后面一样)而已

  5. 接着上面,如果序列化了 A ,添加了 sex 字段,此时还将 serialVersionUID 改为 2 ,此时反序列化就会报错,同样是版本不一致

7.2 序列化的时候,如果某些成员不要序列化,该怎么实现?

使用 transient 关键字。加了 这个关键字的对象 sex ,在反序列化的时候会为null(如果是int 等类型就会是 0) 。

7.3 如果类 A 中有成员未实现可序列化接口,会发生什么?

如果 A 中有成员 User 对象没有实现可序列化接口,序列化的时候会报 “不可序列化”的异常。

7.4 如果类 A 是可序列化的,但是其父类不是,则反序列化后,从父类继承过来的实例变量状态如何?

假如 User 继承了 Person 类,并且实现了 Serializable 接口,但是 Person 类没有实现 Serializable 接口。

即 父类的成员未默认值,Object 类型为 null ,int 等类型为 0。

如果要想父类也实现,需要父类也实现 Serializable 接口,并且有默认的构造函数(无参的,public 的)。

谢谢你的鼓励