1:Java 序列化与反序列化是什么

序列化是将对象转换为可传输格式的过程。 是一种数据的持久化手段。一般广泛应用于网络传输,RMI 和 RPC 等场景中。

反序列化是序列化的逆操作。

序列化是将对象的状态信息转换为可存储或传输的形式的过程。一般是以字节码或 XML 格式传输。而字节码或 XML 编码格式可以还原为完全相等的对象。这个相反的过程称为反序列化。

2:为什么需要序列化与反序列化

在 Java 中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象。但是,我们创建出来的这些 Java 对象都是存在于 JVM 的堆内存中的。只有 JVM 处于运行状态的时候,这些对象才可能存在。一旦 JVM 停止运行,这些对象的状态也就随之而丢失了。

但是在真实的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java 的对象序列化可以帮助我们实现该功能。

3:Java 对象如何实现序列化与反序列化

  • 实现 Java.io.Serializable接口。
  • 实现Externalizable接口

3-1:如何实现序列化

  1. 在 Java 中,只要一个类实现了 Java.io.Serializable 接口,那么它就可以被序列化。
  2. 通过 ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列化
  3. 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)
  4. 序列化并不保存静态变量。
  5. 要想将父类对象也序列化,就需要让父类也实现 Serializable 接口。
  6. Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
  7. 服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。

3-2:Externalizable 和 Serializable 的区别

Externalizable 继承了 Serializable, 该接口中定义了两个抽象方法:writeExternal()readExternal()。 当使⽤ Externalizable 接口来进⾏序列化与反序列化的时候需要开发⼈员重写writeExternal()readExternal()方法。
如果没有在这两个方法中定义序列化实现细节,那么序列化之后,对象内容为空。

实现 Externalizable 接口的类反序列化时会通过无参构造函数创建类再填充数据,所以必须要提供⼀个 public 的⽆参的构造器。

实现 Externalizable, 并实现writeExternal()readExternal()方法可以指定序列化哪些属性。

3-3: 什么是 serialVersionUID

serialVersionUID是用来验证版本一致性的。用于反序列化时保证数据的版本正确。

3-4:序列化协议有哪些

  • xml
  • json

3-5:Serializable 接口并没有方法和字段,为什么只有实现了该接口的类的对象才能被序列化呢?

是因为在ObjectOutputStreamwriteObject0()中进行了判断

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
if (obj instanceof String) {
writeString((String) obj, unshared);
} else if (cl.isArray()) {
writeArray(obj, desc, unshared);
} else if (obj instanceof Enum) {
writeEnum((Enum<?>) obj, desc, unshared);
} else if (obj instanceof Serializable) {
writeOrdinaryObject(obj, desc, unshared);
} else {
if (extendedDebugInfo) {
throw new NotSerializableException(
cl.getName() + "\n" + debugInfoStack.toString());
} else {
throw new NotSerializableException(cl.getName());
}
}

进行序列化操作时,会判断要被序列化的类是否是 Enum、Array 和 Serializable 类型,如果不是则直接抛出 NotSerializableException。

3-6:Java 对象如何实现反序列化

使用ObjectInputSteam

3-7:哪些不会被序列化

使用transient修饰的成员和静态成员属性不会序列化。

3-8:如果类中的一个成员未实现可序列化接口,会发生什么情况?

运行时将引发不可序列化异常NotSerializableException

3-9:如果类是可序列化的, 但其父类不是, 则反序列化后从父类类继承的实例变量的状态如何?

父类的实例变量将通过构造函数初始化。如果想要保留父类的实例变量,父类也需要实现序列化接口

3-10:是否可以自定义序列化过程, 或者是否可以覆盖 Java 中的默认序列化过程?

可以,只要重写writeObject()readObject()方法,就会调用重写的方法。

3-11:假设新类的父类实现序列化接口, 如何避免新类被序列化?

和上一问相同,重写writeObject()readObject()方法而方法内部不做实现。

4:序列化和反序列化应用场景