絮叨

Java序列化其实这个用的多不多,我觉得看公司的技术栈吧,如果用的是cloud那套,估计接触的会少点,但是也不是说没有,如果是dubbo那套的话,就多点,上次我们说Netty的编码解码的时候说到了谷歌的protoBuf,今天的话我们就来好好看看Java序列化,详细的聊聊

一、认识序列化

从网络通信认识序列化

从上面这张图我们可以看到,两个进程进行通信时候,想要发送数据,要先要把数据发送到TCP缓冲区,然后形成报文再发送出去,同样的道理,接收端也是一样。我们可以相互发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。同样的,当两个Java进程进行通信时,也可以使用序列化技术实现对象之间的传递。一句话:Java序列化是指把Java对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为Java对象的过程

序列化的使用场景

这个使用场景应该算是最重要的一环了,因为我们学习序列化就是为了使用他,现在把他们归纳一下:

  • 永久性保存对象,保存对的字节序列到本地文件或者数据库中;
  • 通过序列化以字节流的形式对象在网络中进行传递和接收;
  • 通过序列化在进程间传递对象

序列化有什么好处呢?

  • 实现了数据的持久化,通过序列化可以把数据永久地保存到硬盘上
  • 利用序列化实现远程通信,即在网络上传送对象的字节序列。

java序列化机制

java序列化主要有两个接口,这两个接口的实现方式,我都会给出,但是重点在于serialize接口的实现方式。在这一部分中,先给出序列化基本的代码实现,在下一部分当中再来看序列化有哪些需要注意的问题。OK,现在开始代码实现java的序列化机制。

使用Serializable接口实现序列化(重点,要牢记,第三部分会多次使用)

首先我们定义一个对象类User

接下来,在Test类中去实现序列化和反序列化。

当我们运行序列化方法时候,就可以看到,我们把数据存在了G://Test/template。

同时当我们运行反序列化方法的时候,就可以看到,反序列化成功,结果就不贴出来了,比较简单。

使用Externalizable接口实现序列化

然后就是Test1类,和上面的Test一样,就不再贴出来了。在这里主要看Externalizable和Serializable接口的区别。下面对其进行归纳一下。

  • Externalizable继承自Serializable接口
  • 需要我们重写writeExternal()与readExternal()方法
  • 实现Externalizable接口的类必须要提供一个public的无参的构造器。

到这,java序列化机制的基本使用就讲完了,从上面可以看出,使用起来还是非常简单的。不过,仅仅会基本的使用还不行,想要面试的时候更加的装13,还需要进一步深化。因此下面一部分主要就是对序列化机制的深入分析。还需要说一点,对于Serializable接口实现的序列化方式一定要牢记,因为下面要多次使用

深入分析java序列化机制

serialVersionUID的作用

一句话:其目的是序列化对象版本控制,有关各版本反序列化时是否兼容。如果在新版本中这个值修改了,新版本就不兼容旧版本,反序列化时会抛出InvalidClassException异常。如果修改较小,比如仅仅是增加了一个属性,我们希望向下兼容,老版本的数据都能保留,那就不用修改;如果我们删除了一个属性,或者更改了类的继承关系,必然不兼容旧数据,这时就应该手动更新版本号,即SerialVersionUid。

serialVersionUID有两种显示的生成方式:

  • 一是默认的1L,比如:private static final long serialVersionUID = 1L;
  • 二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,

首先我们在User里面设置一下serialVersionUID=123456L。

接着我们更改serialVersionUID = 123456789L。然后再反序列化,就可以看到如下的错误了。同时也验证了序列化和反序列化需要版本一致的问题。

静态变量的序列化

首先需要说一下,静态变量不会被序列化。因为静态变量在全局区,本来流里面就没有写入静态变量,我打印静态变量当然会去全局区查找,当我write read 同时使用时,内存中的静态变量变了。

下面这个例子,主要是在对静态变量序列化之后,然后更改静态变量age的值,再重新反序列化输出一下。结果会出现两种情况:

  • (1)反序列输出的静态变量值没有变化:说明静态变量被序列化了。

  • (2)反序列输出的静态变量值变化了:说明静态变量没有被序列化了。

然后看一下输出结果

Transient 关键字作用

Transient 关键字的作用是控制变量的序列化,在变量声明前加上该关键字,可以阻止该变量被序列化到文件中,在被反序列化后,transient 变量的值被设为初始值,如 int 型的是 0,对象型的是 null。

这个很好理解也很简单。下面代码来看一下他的作用

  • 第一步:把User里面的年龄属性改成Transient 修饰。private transient int age;

  • 第二步:Test不变,进行序列化和反序列化。

  • 第三步:看结果。反序列化之后的输出age应该为0;

更多推荐

JAVA的序列化