1、引出序列化、反序列化
假如有这样一个场景:
南极到北极有一根细管,南极的一块大冰块如何运到北极呢?
显然南极大冰块需要先化成水,通过细管流到北极,到北极之后再收集起来冻成大冰块。这是一个虚拟的场景,但是和现实生活中的网络数据传输有些相似。
比如现实中一台客户端要发送信息,一个Person对象,需要先变成数据流(这个和刚才的水流相似),再通过网络链路(细管)到达服务器端,服务器端接收到后将数据流转换成Person对象。这样一个过程就实现了消息的传输,其中涉及到了两次数据的转换,称之为:序列化和反序列化。
序列化(Serialization)是将对象的状态信息转化为可以存储或者传输的形式的过程,一般将一个对象存储到一个储存媒介,例如文件或缓存等,在网络传输过程中,可以是字节或者XML等格式;而字节或者XML格式的可以还原成完全相等的对象,这个相反的过程又称为反序列化;
2、Java中的序列化和反序列化
接下来我们说一下Java中的序列化和反序列化,在Java中,我们可以通过多种方式来创建对象,并且只要对象没有被回收我们都可以复用此对象。但是,我们创建出来的这些对象都存在于JVM中的堆(heap)内存中,只有JVM处于运行状态的时候,这些对象才可能存在。一旦JVM停止,这些对象也就随之消失;
但是在真实的应用场景中,我们需要将这些对象持久化下来,并且在需要的时候将对象重新读取出来,Java的序列化可以帮助我们实现该功能。
对象序列化机制(object serialization)是java语言内建的一种对象持久化方式,通过对象序列化,可以将对象的状态信息保存为字节数组,并且可以在有需要的时候将这个字节数组通过反序列化的方式转换成对象,对象的序列化可以很容易的在JVM中的活动对象和字节数组(流)之间进行转换。
对象的序列化和反序列化主要应用在哪些方面?
a、对象状态保存到文件、数据库;
b、网络传输对象;
c、RMI(远程方法调用)
3、Java序列化及反序列化相关接口及类
Java为了方便开发人员将java对象序列化及反序列化提供了一套方便的API来支持,其中包括以下接口和类:
java.io.Serializable
java.io.Externalizable
ObjectOutput
ObjectInput
ObjectOutputStream
ObjectInputStream
接下来通过两种方式演示:
a、网络传输对象
b、对象存入文件
4、网络传输Java对象
因为pwd是transient修饰的,address是static修饰的,所以这两个属性没有参与序列化,服务器端没有得到这两个属性的值。
注:实验时需要分成两个项目:ClientProject包含User、TCPClient;ServerProject包含User、TCPServer,如果两个项目中的User类名和包名路径要保持一致,不然会报异常:
如果其中一个项目中需要加一个新的属性,比如private String s;,而另一个项目中没有加。则序列化时会报错,版本号不一致:
因为默认情况下,Java会根据属性和相关方法计算serialVersionUID的值,所以改动属性后,serialVersionUID 的值会发生改变,进而导致异常。所以为了避免出现这种情况,我们需要手动指定serialVersionUID 的值,比如1L。
我们来看下Serializable类的源码注释说明:
如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java 对象序列化规范”中所述。不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 – serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。
5、Java对象存入文件
意犹未尽?见下一篇......
领取专属 10元无门槛券
私享最新 技术干货