Java - 如果你的Serializable类包含一个不可序列化的成员,会发生什么?你是如何解决的?

分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请点击http://www.captainbed.net

任何序列化该类的尝试都会因NotSerializableException而失败,但这可以通过在Java中为其设置瞬态(trancient)变量来轻松解决。

Java序列化相关的常见问题

Java序列化是一个重要概念,但它很少用作持久性解决方案,开发人员大多忽略了Java序列化API。

大多数商业项目使用数据库或内存映射文件或只是普通文件,来满足持久性要求,只有很少的项目依赖于Java中的序列化过程。

对于那些不熟悉Java序列化的人,Java序列化是用来通过将对象的状态存储到带有.ser扩展名的文件来序列化Java中的对象的过程,并且可以通过这个文件恢复重建Java对象状态,这个逆过程称为deserialization。

什么是Java序列化

序列化是把对象改成可以存到磁盘或通过网络发送到其他运行中的Java虚拟机的二进制格式的过程,并可以通过反序列化恢复对象状态。Java序列化API给开发人员提供了一个标准机制,通过java.io.Serializable和java.io.Externalizable接口,ObjectInputStream及ObjectOutputStream处理对象序列化。Java程序员可自由选择基于类结构的标准序列化或是他们自定义的二进制格式,通常认为后者才是最佳实践,因为序列化的二进制文件格式成为类输出API的一部分,可能破坏Java中私有和包可见的属性的封装。

如何序列化

让Java中的类可以序列化很简单,你的Java类只需要实现java.io.Serializable接口,JVM就会把Object对象按默认格式序列化。让一个类是可序列化的需要有意为之,类可序列化会是一个长期代价,可能会因此而限制你修改或改变其实现。当你通过实现添加接口来更改类的结构时,添加或删除任何字段都可能会破坏默认序列化,这可以通过自定义二进制格式使不兼容的可能性最小化,但仍需要大量的努力来确保向后兼容性。序列化如何限制你更改类的能力的一个示例是SerialVersionUID。如果不显式声明SerialVersionUID,则JVM会根据类结构生成其结构,该结构依赖于类实现的接口和可能更改的其他几个因素。假设你新版本的类文件实现了另一个接口,JVM将生成一个不同的SerialVersionUID,当你尝试加载旧版本的程序序列化的旧对象时,你将获得无效类异常InvalidClassException。

1.Java中的可序列化接口和可外部化接口之间的区别是什么?

这是Java序列化访谈中最常问的问题。下面是我的版本:Externalizable给我们提供writeExternal()和readExternal()方法,这让我们灵活地控制Java序列化机制,而不是依赖于Java的默认序列化。正确实现Externalizable接口可以显著提高应用程序的性能。

2.可序列化的方法有多少?如果没有方法,那么可序列化接口的用途是什么?

可序列化Serializalbe接口存在于java.io包中,构成了Java序列化机制的核心。它没有任何方法,在Java中也称为标记接口。当类实现java.io.Serializable接口时,它将在Java中变得可序列化,并指示编译器使用Java序列化机制序列化此对象。

3.什么是serialVersionUID?如果你不定义这个,会发生什么?

serialVersionUID是一个private static final long型ID,当它被印在对象上时,它通常是对象的哈希码,你可以使用serialver这个JDK工具来查看序列化对象的serialVersionUID。SerialVerionUID用于对象的版本控制。也可以在类文件中指定serialVersionUID。不指定serialVersionUID的后果是,当你添加或修改类中的任何字段时,则已序列化类将无法恢复,因为为新类和旧序列化对象生成的serialVersionUID将有所不同。Java序列化过程依赖于正确的序列化对象恢复状态,并在序列化对象序列版本不匹配的情况下引发java.io.InvalidClassException无效类异常。

4.序列化时,你希望某些成员不要被序列化?你如何实现?

如果你不希望任何字段是对象的状态的一部分,根据你的需要,声明它为静态或瞬态,这样就不会是在Java序列化过程中被包含在内。

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

如果尝试序列化实现可序列化的类的对象,但该对象包含对不可序列化类的引用,则在运行时将引发不可序列化异常NotSerializableException,在可序列化类中添加新字段时要注意。

6.如果类是可序列化的,但其超类不是,则反序列化后从超类继承的实例变量的状态如何?

Java序列化过程仅在对象层次都是可序列化的结构中继续,即实现Java中的可序列化接口,并且从超类继承的实例变量的值将通过调用构造函数初始化,在反序列化过程中不可反序列化超类。一旦构造函数链启动,就不可能停止,因此,即使层次结构中较高的类实现可序列化接口,也将执行构造函数。

7.是否可以自定义序列化过程,或者是否可以覆盖Java中的默认序列化过程?

答案是肯定的,你可以。我们都知道,对于序列化一个对象需调用ObjectOutputStream.writeObject(saveThisObject),并用ObjectInputStream.readObject()读取对象,但Java虚拟机为你提供的还有一件事,是定义这两个方法。如果在类中定义这两种方法,则JVM将调用这两种方法,而不是应用默认序列化机制。你可以在此处通过执行任何类型的预处理或后处理任务来自定义对象序列化和反序列化的行为。需要注意的重要一点是要声明这些方法为私有方法,以避免被继承、重写或重载。由于只有Java虚拟机可以调用类的私有方法,你的类的完整性会得到保留,并且Java序列化将正常工作。

8.假设新类的超级类实现可序列化接口,如何避免新类被序列化?

如果类的Super类已经在Java中实现了可序列化接口,那么它在Java中已经可以序列化,因为你不能取消接口,它不可能真正使它无法序列化类,但是有一种方法可以避免新类序列化。为了避免Java序列化,你需要在类中实现writeObject()和readObject()方法,并且需要从该方法引发不可序列化异常NotSerializableException。这是自定义Java序列化过程的另一个好处。

9.在Java中的序列化和反序列化过程中要用到哪些方法?

Java序列化由java.io.ObjectOutputStream类完成。该类是一个筛选器流,它封装在较低级别的字节流中,以处理序列化机制。要通过序列化机制存储任何对象,我们调用ObjectOutputStream.writeObject(savethisobject),要反序列化该对象,我们调用ObjectInputStream.readObject()方法。调用writeObject()方法在Java中触发序列化过程。关于readObject()方法,需要注意的一点且是很重要一点是,它用于持久性读取字节,并从这些字节创建对象,并返回一个对象,该对象需要强制类型转换为正确的类型。

10.假设你有一个类,它序列化并存储在持久性存储中,然后修改了该类以添加新字段。如果对已序列化的对象进行反序列化,会发生什么情况?

这取决于类是否具有其自己的serialVersionUID。正如我们从上面的问题知道,如果我们不提供serialVersionUID,则Java编译器将生成它,通常它等于对象的哈希代码。通过添加任何新字段,有可能为该类新版本生成的新serialVersionUID与已序列化的对象不同,在这种情况下,Java序列化API将引发java.io.InvalidClassException,因此建议在代码中拥有自己的serialVersionUID,并确保在单个类中始终保持不变。

11.Java序列化机制中的兼容更改和不兼容更改是指什么?

真正的挑战在于通过添加任何字段、方法或删除任何字段或方法来更改类结构,方法是使用已序列化的对象。根据Java序列化规范,添加任何字段或方法都面临兼容的更改和更改类层次结构或取消实现的可序列化接口,有些接口在非兼容更改下。对于兼容和非兼容更改的完整列表,建议阅读Java序列化规范。

12.我们可以通过网络传输一个序列化的对象吗?

是的,你可以通过网络传输序列化对象,因为Java序列化对象仍以字节的形式保留,字节可以通过网络发送。你还可以将序列化对象存储在磁盘或数据库中作为Blob。

13.在Java序列化期间,哪些变量未被序列化?

这个问题问得不同,但目的还是一样的,Java开发人员是否知道静态和瞬态变量的细节。由于静态变量属于类,而不是对象,因此它们不是对象状态的一部分,因此在Java序列化过程中不会保存它们。由于Java序列化仅保留对象的状态,而不是对象本身。瞬态变量也不包含在Java序列化过程中,并且不是对象的序列化状态的一部分。

你可能感兴趣的:(Java)