1:Java 序列化与反序列化是什么
序列化是将对象转换为可传输格式的过程。 是一种数据的持久化手段。一般广泛应用于网络传输,RMI 和 RPC 等场景中。
反序列化是序列化的逆操作。
序列化是将对象的状态信息转换为可存储或传输的形式的过程。一般是以字节码或 XML 格式传输。而字节码或 XML 编码格式可以还原为完全相等的对象。这个相反的过程称为反序列化。
2:为什么需要序列化与反序列化
在 Java 中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用该对象。但是,我们创建出来的这些 Java 对象都是存在于 JVM 的堆内存中的。只有 JVM 处于运行状态的时候,这些对象才可能存在。一旦 JVM 停止运行,这些对象的状态也就随之而丢失了。
但是在真实的应用场景中,我们需要将这些对象持久化下来,并且能够在需要的时候把对象重新读取出来。Java 的对象序列化可以帮助我们实现该功能。
3:Java 对象如何实现序列化与反序列化
- 实现
Java.io.Serializable
接口。 - 实现
Externalizable
接口
3-1:如何实现序列化
- 在 Java 中,只要一个类实现了 Java.io.Serializable 接口,那么它就可以被序列化。
- 通过 ObjectOutputStream 和 ObjectInputStream 对对象进行序列化及反序列化
- 虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化 ID 是否一致(就是 private static final long serialVersionUID)
- 序列化并不保存静态变量。
- 要想将父类对象也序列化,就需要让父类也实现 Serializable 接口。
- Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。
- 服务器端给客户端发送序列化对象数据,对象中有一些数据是敏感的,比如密码字符串等,希望对该密码字段在序列化时,进行加密,而客户端如果拥有解密的密钥,只有在客户端进行反序列化时,才可以对密码进行读取,这样可以一定程度保证序列化对象的数据安全。
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 接口并没有方法和字段,为什么只有实现了该接口的类的对象才能被序列化呢?
是因为在ObjectOutputStream
的writeObject0()
中进行了判断
1 | if (obj instanceof String) { |
进行序列化操作时,会判断要被序列化的类是否是 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()
方法而方法内部不做实现。