JavaRush /Java 博客 /Random-ZH /采访中有关连载的 13 个常见问题
Dmitry Vasilyev
第 26 级
Саратов

采访中有关连载的 13 个常见问题

已在 Random-ZH 群组中发布
文章翻译https://javarevisited.blogspot.com/2011/04/top-10-java-serialization-interview.html Java中的序列化是什么?序列化是重要的概念之一,很少用作保存程序状态的解决方案,因此该 API 经常被开发人员忽视。然而,根据我的经验,序列化在任何基础 Java 面试中都是一个非常重要的主题。我遇到的几乎每次面试都会涉及一两个有关连载的问题,而且我发现应聘者在问了几个有关该主题的问题后,会因为自己缺乏这方面的经验而感到不舒服。他们不知道如何在Java中序列化一个对象,他们不熟悉任何序列化的例子,无法解释其工作机制,瞬态变量和易失性变量之间的区别,他们不知道Serialized接口有多少方法有。什么是标记接口?它的目的是什么?Java 中的外部化和序列化实现有什么区别?为什么java引入注解后不把Serialized替换成@Serialized呢?在本文中,我们将涵盖针对初学者和高级开发人员的问题,这些问题对每个人(从初级开发人员到高级开发人员)同样有用。大多数商业项目使用数据库、内存映射文件或只是普通的平面文件来提供更高的鲁棒性,但很少有项目依赖于 Java 的序列化过程。无论如何,这篇文章不是教程 - 它是关于在参加任何 Java 面试之前值得自己澄清的问题,并对自己不熟悉的术语感到惊讶。对于那些完全不熟悉 Java 序列化的人:“Java 中的序列化是一个通过将对象的状态存储在扩展名为 .ser 的文件中并重新创建对象的状态来序列Java 中的对象的过程。从这个文件。这个相反的过程称为反序列化。 塞普尔卡 Java Serialization API 为开发人员提供了使用 Serialized 和Externalized 接口序列化对象的标准机制。顺便说一句,这篇文章是我(不是我,译者,而是英文原文作者)之前文章的延续:设计模式的20道面试题Java中单例模式的10道面试题。那么,我们走吧! Java中的序列化是什么? Java 中的对象序列化是用于将对象转换为二进制格式的过程,该二进制格式可以保存到磁盘或通过网络发送到任何其他正在运行的 Java 虚拟机;从二进制流创建对象的逆过程称为反序列化。Java提供了API,其中包括java.io.Serialized、java.io.Externalizable、ObjectInputStream和ObjectOutputStream等。程序员可以自由使用Java基于类结构使用的默认序列化机制,但他们也可以使用自己的自定义二进制格式,这通常被推荐作为序列化最佳实践,因为序列化二进制格式成为类导出API的一部分并且可能会破坏 Java 中私有和包私有字段提供的封装。一般来说,这些信息足以开始使用。 如何使 Java 类可序列化? 这很容易。您的类只需要实现 java.io.Serialized 接口,JVM 将负责以默认格式序列化对象。应该简短地做出创建可序列化类的决定,因为虽然创建可序列化类的短期成本很低,但长期成本却很高,并且可能会限制您对实现进行进一步修改的能力。发生这种情况的原因是,与任何公共 API 一样,对象的序列化形式成为公共 API 的一部分,并且当您通过实现添加接口来更改类的结构时,添加或删除任何字段都可能会破坏默认序列化。不过,可以通过使用自定义二进制格式来最大程度地减少这种情况,但仍然需要付出很大的努力来确保向后兼容性。SerialVersionUID 字段是序列化如何限制您修改类的能力的一个示例。如果您没有显式声明 SerialVersionUID,那么虚拟机会根据类结构生成它,这取决于类实现的接口以及其他几个可以更改的因素。假设您实现了与 JVM 不同的接口,它将为新版本的类文件生成不同的 SerialVersionUID,并且当您尝试加载由旧版本程序序列化的旧对象时,您将得到一个无效类异常。 问题1)Java中Serialized接口和Externalized接口有什么区别? 这是最常见的 Java 序列化面试问题。Externalized接口为我们提供了writeExternal()和readExternal()方法,这使我们能够灵活地控制序列化,而不是依赖于默认的机制。正确实现Externalized接口可以显着提高应用程序性能。 问题2)Serialized有多少个方法?如果没有方法,那么Serialized接口的用途是什么? 可序列化接口存在于java.io包中,构成了Java序列化引擎的核心。它没有任何方法,在Java中也称为标记接口。当您的类实现 java.io.Serialized 接口时,它就变得可序列化。这很简单。 问题3)什么是serialVersionUID?如果不定义会发生什么? 我最喜欢的 Java 序列化面试问题之一。SerialVersionUID是对象序列化时附加的标识符,通常是对象的哈希码。可以使用serialver工具获取序列化对象的serialVersionUID。SerialVersionUID 用于对象版本控制。您还可以在类文件中手动指定serialVersionUID。不指定serialVersionUID的后果是,如果添加或更改类中的任何字段,已经序列化的类将无法恢复,因为为新类生成的serialVersionUID将与旧序列化对象的相同字段不同。Java 序列化过程依赖于正确的serialVersionUID 来恢复序列化对象的状态,如果不匹配,则会抛出java.io.InvalidClassException。要了解有关serialversionuid 的更多信息,请参阅此处问题4)序列化时,是否希望某些成员不被序列化?如何实现这一目标? 另一个常见的连载面试问题。有时人们还会问如何使用瞬态变量,瞬态变量和静态变量是否序列化等等,因此,如果您不希望任何字段成为对象状态的一部分,请将其声明为静态或瞬态,具体取决于根据您的需要,它不会包含在 Java 序列化过程中。 问题 5)如果类成员之一没有实现 Serialized 接口,会发生什么情况? 关于Java中序列化过程的简单问题之一。如果您尝试序列化实现了 Serialized 的类的对象,但该对象包含对不可 Serialized 的类的引用,则在运行时将抛出 NotSerializedException ,这就是为什么我总是在我的代码),最好的代码注释技术之一是指导开发人员在向 Serialized 类添加新字段时记住这一事实。 问题6)如果一个类是可序列化的,但它的超类不是可序列化的,那么从超类继承的实例变量在反序列化后会是什么状态? 只要类实现了 Serialized 接口,Java 序列化过程就只会在对象层次结构中继续,并且从超类继承的变量的值将在反序列化过程中通过调用不可序列化超类的构造函数来初始化。一旦构造函数链启动,就无法停止它,因此即使层次结构中较高的类(不)实现 Serialized 接口构造函数也会被执行。这个连载面试题看起来可能很难,但是如果你熟悉了关键概念,就不难了。 问题 7) 可以自定义序列化过程或覆盖 Java 中的默认序列化过程吗? 答案是肯定的,可以。我们都知道,要序列化一个对象,需要调用 ObjectOutputStream.writeObject(saveThisObject) ,要读取一个对象,需要调用 ObjectInputStream.readObject() ,但是 Java 虚拟机还为您提供了一项功能 - 定义这两个方法在你的班级里。如果您在类中定义它们,JVM 将调用这两个方法,而不是使用默认的序列化机制。您可以在此处通过执行任何预处理或后处理任务来配置对象的序列化和反序列化行为。需要注意的是,这些方法必须是私有的,以避免继承、覆盖或重载。由于只有 Java 虚拟机可以调用私有方法,因此类的完整性将被保留,并且序列化将照常工作。在我看来,这是在 Java 序列化面试中可以提出的最好问题之一。一个很好的后续问题是:为什么需要为对象提供自定义序列化形式? 问题8)假设一个新类的超类实现了Serialized接口,我们如何避免新类被序列化? 关于 Java 序列化的面试难题之一。如果一个类的超类已经实现了 Java 中的 Serialized 接口,那么其后代类也是可序列化的,因为你不能不实现父类的接口,并且实际上不可能将其设为不可序列化类。但是,有一种方法可以避免这个新类的序列化。为此,您需要实现 writeObject() 和 readObject() 方法,并从这些方法中抛出 NotSerializedException。随着面试的进行,这个问题通常会作为附加问题被问到。 问题9)Java中序列化和反序列化过程中使用了哪些方法? 这是序列化中非常常见的问题。在这个案例中,面试官想了解什么?无论您是否熟悉 readObject()、writeObject()、readExternal() 和 writeExternal() 的使用。Java 序列化是由 java.io.ObjectOutputStream 类完成的。此类是一个过滤流,包裹在较低级别的字节流中以处理序列化引擎。要使用序列化机制保存任何对象,我们调用 ObjectOutputStream.writeObject(saveThisObject) 并反序列化该对象,我们调用 ObjectInputStream.readObject() 方法。调用 writeObject() 方法启动序列化过程。关于 readObject() 方法需要注意的一件重要事情是,它用于读取字节并从这些字节创建和返回一个对象,而该对象又必须转换为正确的类型。 问题 10) 假设您有一个已序列化并保存的类,然后您修改该类以添加新字段。如果反序列化一个已经序列化的对象会发生什么? 这取决于该类是否有自己的serialVersionUID。从上面的问题中我们知道,如果我们在代码中不提供serialVersionUID,java编译器将自行生成它,并且通常它等于该对象的哈希码。添加任何新字段后,为该版本的类生成的新serialVersionUID有可能与已经序列化的对象不匹配,在这种情况下,API将抛出java.io.InvalidClassException。因此,建议在代码中拥有自己的serialVersionUID,对于同一个类来说始终是相同的。 问题11)Java序列化机制中兼容和不兼容的变化有哪些? 真正的问题是通过添加任何字段、方法或删除具有已序列化对象的任何字段或方法来更改类的结构。根据 Java 序列化规范,添加任何字段或方法都属于兼容更改和更改类层次结构或未实现可序列化接口(某些属于不兼容更改)。有关兼容和不兼容更改的完整列表,我建议阅读 Java 序列化规范。 问题 12)我们可以通过网络传输序列化对象吗? 是的,您可以通过网络传递序列化对象,因为 Java 序列化对象是可以以任何方式传递的字节集合。您还可以将序列化对象存储在磁盘上或作为 Blob 存储在数据库中。 问题13)Java序列化时哪些类型的变量不被序列化? 有时会以不同的方式提出这个问题,但目标是相同的:了解 Java 开发人员是否了解序列化静态变量和瞬态变量的细节。由于静态变量属于类而不是对象,因此它们不是对象状态的一部分,因此它们在 Java 序列化过程中不会被持久化。由于Java序列化仅存储对象的状态而不存储对象本身,因此瞬态变量也不包含在序列化过程中,并且不是对象序列化状态的一部分。提出这个问题后,或许面试官会问:如果不存储这些变量的值,那么反序列化并重新创建这个对象后,这些变量的值会是什么呢?而这一点,各位同事,你们自己想一想:)原文在这里
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION