文章目录

  • 说说你对序列化的理解
  • Serializable接口为什么需要定义serialVersionUID变量?
  • 除了Java自带的序列化之外,你还了解哪些序列化工具?



在这里先说一下什么是序列化以及为什么需要序列化


序列化机制可以将对象转换成字节序列,这些字节序列可以保存在磁盘上,也可以在网络中传输,并允许程序将这些字节序列再次恢复成原来的对象。其中,对象的序列化(Serialize),是指将一个Java对象写入IO流中,对象的反序列化(Deserialize),则是指从IO流中恢复该Java对象。

若对象要支持序列化机制,则它的类需要实现Serializable接口,该接口是一个标记接口,它没有提供任何方法,只是标明该类是可以序列化的,Java的很多类已经实现了Serializable接口,如包装类、String、Date等。

若要实现序列化,则需要使用对象流ObjectInputStream和ObjectOutputStream。其中,在序列化时需要调用ObjectOutputStream对象的writeObject()方法,以输出对象序列。在反序列化时需要调用ObjectInputStream对象的readObject()方法,将对象序列恢复为对象。

ObjectOutputStream oos = new ObjectOutputStream(new 
	 	FileOutputStream(new File("D://desktop//1.jpg")));
        oos.writeObject("123");
        ObjectInputStream ois = new ObjectInputStream(new 
        FileInputStream(new File("D://desktop//1.jpg")));
        Object o = ois.readObject();
        System.out.println(o);

说说你对序列化的理解

面试官:说说你对Java序列化的理解
我:之所以需要序列化,是为了解决网络通信过程中对象传输的一个问题。也就是说如何把当前JVM进程中的对象跨网络传输到另一个JVM进程中进行恢复。
我:而序列化就是把内存中的对象转换为字节流,以便用来实现存储和传输。
我:而反序列化就是把根据文件或者网络上传输过来的对象字节流,根据字节流里面的对象描述信息和状态重新构建一个新的对象。
我:序列化的前提是为了保证通信双方对于对象的可识别性, 所以很多时候我们会先把对象转换为通用的解析格式,比如JSON,XML,然后再把他们转换为字节流进行传输。从而实现跨平台或者跨语言的一种可识别性。
我:目前序列化的开源技术常用的有JSON,XML,Avro等。
我:但是实际那个序列化技术更加合适,一般需要考虑一下几个因素:
我:1:序列化的数据大小,因为数据大小会影响传输性能。
我:2:序列化的性能,序列化耗时太长会影响业务性能。
我:3:是否支持跨平台或者跨语言。
我:4:技术的成熟度,越成熟的方案,使用的公司越多,也就越稳定。
面试官:行,下一题。

Serializable接口为什么需要定义serialVersionUID变量?

serialVersionUID代表序列化的版本,通过定义类的序列化版本,在反序列化时,只要对象中所存的版本和当前类的版本一致,就允许做恢复数据的操作,否则将会抛出序列化版本不一致的错误。

如果不定义序列化版本,在反序列化时可能出现冲突的情况,例如:

  1. 创建该类的实例,并将这个实例序列化,保存在磁盘上;
  2. 升级这个类,例如增加、删除、修改这个类的成员变量;
  3. 反序列化该类的实例,即从磁盘上恢复修改之前保存的数据。

在第3步恢复数据的时候,当前的类已经和序列化的数据的格式产生了冲突,可能会发生各种意想不到的问题。增加了序列化版本之后,在这种情况下则可以抛出异常,以提示这种矛盾的存在,提高数据的安全性。

需要注意的是,如果没有显示定义serialVersionUID,则JVM会根据类的信息自动计算出它的值,如果升级前后类的内容发生了变化,该值的计算结果通常就不同,这会导致反序列化的失败。所以,最好在打算序列化的类中显示地定义serialVersionUID,这样即便在序列化后它对应的类被修改了,由于版本号是一致的,所以该对象依然可以被正确的反序列化。

如果类的修改会导致反序列化失败,则应该为此类分配新的serialVersionUID,那么对类的哪些内容进行修改会导致反序列化失败呢?

  • 如果修改类时只是修改了方法,则反序列化不受影响。
  • 如果修改类时只是修改了静态变量,则反序列化不受影响。
  • 如果修改类时改变了实例变量,则可能导致反序列化失败。

除了Java自带的序列化之外,你还了解哪些序列化工具?

  • JSON:目前使用比较频繁的格式化数据工具,简单直观,可读性好,有jackson,gson,fastjson等等,比较优秀的JSON解析工具的表现还是比较好的,有些json解析工具甚至速度超过了一些二进制的序列化方式。
  • Thrift:是Facebook开源提供的一个高性能,轻量级RPC服务框架,其产生正是为了满足当前大数据量、分布式、跨语言、跨平台数据通讯的需求。 但是,Thrift并不仅仅是序列化协议,而是一个RPC框架。 相对于JSON和XML而言,Thrift在空间开销和解析性能上有了比较大的提升,对于对性能要求比较高的分布式系统,它是一个优秀的RPC解决方案。但是由于Thrift的序列化被嵌入到Thrift框架里面, Thrift框架本身并没有透出序列化和反序列化接口,这导致其很难和其他传输层协议共同使用(例如HTTP)。
  • Avro:提供两种序列化格式,即JSON格式或者Binary格式。Binary格式在空间开销和解析性能方面可以和Protobuf媲美, JSON格式方便测试阶段的调试。 Avro支持的数据类型非常丰富,包括C++语言里面的union类型。Avro支持JSON格式的IDL和类似于Thrift和Protobuf的IDL(实验阶段),这两者之间可以互转。Schema可以在传输数据的同时发送,加上JSON的自我描述属性,这使得Avro非常适合动态类型语言。 Avro在做文件持久化的时候,一般会和Schema一起存储,所以Avro序列化文件自身具有自我描述属性,所以非常适合于做Hive、Pig和MapReduce的持久化数据格式。对于不同版本的Schema,在进行RPC调用的时候,服务端和客户端可以在握手阶段对Schema进行互相确认,大大提高了最终的数据解析速度。