Java 基础面试300题 (291-313)
291 . Externalizable
接口和Serializable
接口有什么区别?
Serializable
接口是一个标记接口,没有定义任何方法,不必实现。Externalizable
接口定义了readExternal()
和writeExternal()
方法,必须实现这些方法。
当实现Serializable
接口时,JVM会和效率低下文件流打交通。
如果不确定如何高效执行IO流,最好使用Serializable
接口实现序列化 。如果能够高效地执行特定于应用程序的IO流,则应考虑通过实现Externalizable
接口来实现序列化。
当对象的所有或大部分属性必须序列化时,使用Serializable
接口并根据需要使用瞬态变量实现序列化将更有效率。但是,如果仅仅是序列化一个具有很多属性的大型Java对象的某些动态属性 ,实现Externalizable
接口是一种更好的序列化方式,因为可以在其重写方法中指定特定的序列化的内容。
292.什么是serialVersionUID
?
serialVersionUID
是可序列化类的唯一标识符,用来确保序列化和反序列化对象引用的是同一类版本。如果未在程序中定义,Java编译器会创建一个唯一的serialVersionUID
。最好为每个可序列化的类都定义一个serialVersionUID
,否则JVM在版本更改时将无法识别该类。每次更改可序列化类的属性时,如果用户没有定义,JVM都会创建一个新的serialVersionUID
。因此,最佳实践是为Java中的所有可序列化类都定义serialVersionUID
。
293. 列举一些类似对象序列化的技术?
Java使用对象序列化将数据永久存储在系统存储中。同样也可以使用其他方法,如数据库、XML和JSON 。使用数据库存储对象是一种非常常见的方法。可以使用ORM或对象关系映射将对象存储到数据库,然后从数据库检索。许多WebService 普遍使用基于XML的数据存储和传输, 这种方式是通过互联网传输数据的最流行方式。JSON数据传输是一种相对较新的使用很广泛的格式。JSON的实现非常简单,它基于Javascript技术,它已经集成到大多数网络浏览器中。
294.Java对象如何在JVM 的生命周期之外存在?
对象序列化允许Java对象生存超过JVM的生命周期。
295. 如何序列化一个集合, 必须让所有成员都可序列化吗?
集合或数组的所有成员都必须可序列化才能序列化。
296.如何从对象的序列化状态中排除某些变量?
要从序列化过程中排除变量,应该用transient
关键字标记这些变量 。如下示例:
transient private String firsName
296 . 如果一个类实现了Serializable
接口 ,但它包括一个不可序列化的对象,是否可以对这个类序列化?
对这个类的序列化将失败,运行时会抛出 java.io.NotSerializableException
异常 。
297. 反序列化完成后,瞬态变量是什么值?
瞬态变量在反序列化完成后会获得默认值。
298: 通过实现Serializable
接口来实现类的序列化,需要实现哪些方法?
不需要实现任何方法。 Serializable
接口是一个标记接口,没有定义任何方法,因此实现它的类不需要实现任何方法。
299. 使用serialVersionUID
的目的是什么?
serialVersionUID
为每个可序列化的类提供版本系统。在反序列化过程中,serialVersionUID
用于检查从输入流读取的数据是否与当前类定义兼容。
300.serialVersionUID
是一个静态字段, 它也需要序列化吗?
serialVersionUID
是一个静态字段,也与其他数据一起序列化。在反序列化期间, 反序列化的serialVersionUID
必须与当前类定义中声明的serialVersionUID
匹配。
301. 如果反序列化对象的serialVersionUID
与类定义中声明的不匹配,会发生什么?
这种情况下,反序列化会失败,抛出java.io.InvalidClassException
异常。
302.如果一个可序列化类没有定义 serialVersionUID
,编译器如何处理?
Java编译器根据类中声明的字段自动增加一个serialVersionUID
。
303.在哪些场景中应该使用序列化?
比如,当需要通过网络发送对象时,对象首先需要序列化,然后才能够(作为数据)发送。
304. 出现NotSerializableException
的原因是什么?
如果要序列化一个类,它必须实现Serializable
接口。同样重要的是,该类中包含的所有对象也是可序列化的。如果任何一个包含的对象没有实现Serializable
接口,则会抛出NotSerializableException
。
305.如何更改默认的序列化行为?
通过重写ObjectOutputStream
对象的writeObject()
和ObjectInputStream
的readObject()
方法可以控制复杂的对象序列化过程,可以提供额外的信息来序列化和反序列化对象。
306.如果想完全控制类的序列化过程,需要实现哪个接口?
如果想完全控制类的序列化过程,应实现 java.io.Externalizable
接口。
307. 实现外部序列化时要重写哪些方法?
实现Externizable
接口时,应重写该接口定义的两个方法readExternal
和writeExternal
。
308. 考虑以下情况,Child
类也是可序列化的吗?
public class Parent implements Serializable
public class Child extends Parent
子类从其对象层次结构中继承了Serializable
接口 ,因此也是可序列化的。
309. 反序列化时,会调用对象的构造函数,是正确的吗?
反序列化意味着恢复序列化对象,而不是重建它。在反序列化过程中没有调用构造函数。
310. 考虑如下代码,Student
类是可序列化的, 它有一个 String
类型的name
实例变量, 因为不是基础类型,序列化会失败吗?
public class Student implements Serializable {
private String name;
...
}
java.lang.String
本身是可序列化的,因此Student
类是可以序列化的类。
311. 所有基础类型的包装类都是可序列化吗?
是的。所有基础数据类型的包装类都实现了可序列化接口。
312.假设有一个可序列化类,但其超类没有实现 Serializable
接口。超类定义了无参数构造函数和字符串参数构造函数。在反序列化时,超级类的哪个构造函数将被调用?
无参数构造函数将被调用, 实现Serializable的子类应该可以访问这个构造函数。
313. 考虑以下情况,有一个可序列化类,但没有定义serialVersionUID
。现在将其序列化,然后在类中添加一个新的实例变量,并将其已序列化的实例反序列化。会发生什么?
由于serialVersionUID
没有在类中定义,JVM会基于类中的字段信息自动生成。一旦在类中添加或删除实例变量,JVM会重新生成serialVersionUID
。在上述场景中,当前类中的serialVersionUID
和序列化的实例中的serialVersionUID
不一致,因为重新增加了实例变量,因此反序例化会失败,抛出java.io.InvalidClassException
异常。