1.什么是Java序列化与反序列化?
序列化:将java对象转换为字节序列的过程叫做序列化及Serialization(序列化)是一种将对象以一连串的字节描述的过程;
反序列化:将字节对象转换为java对象的过程叫做反序列化及是一种将这些字节重建成一个对象的过程。Java序列化API提供一种处理对象序列化的标准机制。
序列化:将对象变为--------------------------->文件中数据
反序列化:将文件中的数据------------------------>转换为对象
2.为什么需要Java序列化与反序列化?
(1)、存储
在java系统运行过程中,java对象只存在于堆栈中,但是一旦系统停止运行了,或者某次交互回话结束后,这些java对象也不存在了,
想要再次获得java对象,则需要从一些存储设备中读取数据,并且还原为原先的java对象,系统才可以再次正常运行。
这里存储设备,可以是文件,缓存(比如EhCache,MemCache,redis等等),甚至是数据库中,都可以,保存这些对象的字节流,等待java对象再次复活!
(2)、传输&交换
分布式系统中,系统之间交互,比如通过java原生的RMI远程调用,返回的字节流反序列化为java对象,才能在jvm里运行。
Java中,一切都是对象,在分布式环境中经常需要将Object从这一端网络或设备传递到另一端。 这就需要有一种可以在两端传输数据的协议。Java序列化机制可以做到。
或者当下常用的分布式架构,http+json返回的json数据,也是需要反序列化为java对象,只是用的方法和jdk原生不同罢了,
以及新的分布式架构,thrift,dubbo等,返回的数据是字节流的形式,也是需要反序列化。
3.怎么Java序列化与反序列化?
java JDK自带的序列化工具有
java.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。
对象序列化一般包括如下步骤:
1) 创建一个对象输出流,它可以包装一个其他类型的目标输出流,如文件输出流;
2) 通过对象输出流的writeObject()方法写对象。
对象反序列化的步骤如下:
1) 创建一个对象输入流,它可以包装一个其他类型的源输入流,如文件输入流;
2) 通过对象输入流的readObject()方法读取对象
特殊说明下:实现Serializable接口的java对象,有一个serialVersionUID,serialVersionUID: 字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量,
同一个java对象只要序列化ID相同,属性相同,那么反序列化会正常的执行
同一个java对象,如果序列化ID不同,属性相同,反序列化都不会成功
因为默认的JDK序列化与反序列化方法要求必须实现Serializable接口,并且序列化ID相同,这时jvm才认可字节流是可以反序列化的,
4.有没有造好的轮子?
java序列化的框架目前也不少,具体实现没来得及一一研究,有基于json反序列化的(GSON,Jackson),有基于字节流实现的(protobuf),以及基于xml等等
5.应用实例:
spring-data-redis 存数据的时候要序列化,取数据的时候要反序列化
当需要把一个对象以hash存储在redis中时候,我是很轻松愉快的写完了。
然后写测试用例,相应的实体bean属性存值居然为null,debug多次,发现还是null值。
于是找到我的bean类,将其 implements Serializable 感觉这下应该 test success 了,
可但是,还特么是null。
我的bean类已经实现了序列化接口,安静了片刻,去看bean的父类fatherbean,父类fatherbean 并没有implements Serializable,然后果断将父类fatherbean implements Serializable, 再一测,这时真正的 test success 了
那么书上是这么说的。。
当父类继承serializable 接口时,所有的子类都可以被序列化
子类实现了serializable接口,父类没有,父类中的属性不能被序列化(不报错,数据会丢失),但是在子类中属性仍能正确序列化。
如果序列化的属性是对象,则这个对象也必须实现serializable接口,否则会报错
在反序列化时,如果对象的属性有修改或删减,则修改的部分属性会丢失,但不会报错
在反序列化时,如果serialVersionUID被修改,则反序列化时回失败
6.反思下:
1) Serializable 和 Externalizable 接口有何不同?
2) Serializable 接口有几个方法? 如果没有方法,那么为什么会有这样的接口?
3) serialVersionUID 有何用途? 如果没定义会有什么问题?
4) 如果你不希望某些成员被序列化,应该怎么做?
5) 序列化某个对象时,如果某个成员没有实现 Serializable 会怎样?
6) 如果某个类实现了 Serializable ,但父类没实现,那么当执行反序列化的时候,对象状态是怎样的?
7) 能否自定义序列化的过程?
8) 如果超类实现了 Serializable,那么之类如何避免被序列化?
9) Java 在进行对象序列化和反序列化的时候,用到了哪个方法?
10) 如果你已经将某个类的实例序列化到磁盘,这时候再往这个类添加新的属性,那么反序列化该对象的时候会发生什么?
11) 什么是 Java 序列化机制中的兼容和不兼容的变化?
12) 能否通过网络来传输序列化对象?
13) 什么类型的变量不会被序列化?
Transient修饰的属性,不会被序列化到文件里,原来初始化的是什么值,最终保持的就是什么值。