点击上方蓝色“程序猿DD”,选择“设为星标”

回复“资源”获取独家整理的学习资料!

作者 | riemann_

来源 | blog.csdn.net/riemann_/article/details/87835260

遇到这个 Java Serializable 序列化这个接口,我们可能会有如下的问题

  • 什么叫序列化和反序列化

  • 作用。为啥要实现这个 Serializable 接口,也就是为啥要序列化

  • serialVersionUID 这个的值到底是在怎么设置的,有什么用。有的是1L,有的是一长串数字,迷惑ing。

我刚刚见到这个关键字 Serializable 的时候,就有如上的这么些问题。

在处理这个问题之前,你要先知道一个问题,这个比较重要。这个Serializable接口,以及相关的东西,全部都在 Java io 里面的。

一、序列化和反序列化的概念

  • 序列化:把对象转换为字节序列的过程称为对象的序列化。

  • 反序列化:把字节序列恢复为对象的过程称为对象的反序列化。

上面是专业的解释,现在来点通俗的解释。在代码运行的时候,我们可以看到很多的对象(debug过的都造吧),可以是一个,也可以是一类对象的集合,很多的对象数据,这些数据中,有些信息我们想让他持久的保存起来,那么这个序列化。

就是把内存里面的这些对象给变成一连串的字节描述的过程。

常见的就是变成文件

我不序列化也可以保存文件啥的呀,有什么影响呢?我也是这么问的。

二、什么情况下需要序列化

  • 当你想把的内存中的对象状态保存到一个文件中或者数据库中时候;

  • 当你想用套接字在网络上传送对象的时候;

  • 当你想通过RMI传输对象的时候;

(老实说,上面的几种,我可能就用过个存数据库的)

三、如何实现序列化

实现Serializable接口即可

上面这些理论都比较简单,下面实际代码看看这个序列化到底能干啥,以及会产生的bug问题。

先上对象代码,FlyPig.java

package com.test;import java.io.Serializable;public class FlyPig implements Serializable {//  private static final long serialVersionUID = 1L;private static String AGE = "269";private String name;private String color;transient private String car;private String addTip;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}public String getCar() {return car;}public void setCar(String car) {this.car = car;}public String getAddTip() {return addTip;}public void setAddTip(String addTip) {this.addTip = addTip;}@Overridepublic String toString() {return "FlyPig{" +"name='" + name + '\'' +", color='" + color + '\'' +", car='" + car + '\'' +", AGE='" + AGE+ '\'' +'}';}
}

注意下,注释的代码,是一会儿要各种情况下使用的。

下面就是main方法啦

package com.test;import java.io.*;public class SerializableTest {public static void main(String[] args) throws Exception {serializeFlyPig();FlyPig flyPig = deserializeFlyPig();System.out.println(flyPig.toString());}/*** 序列化*/private static void serializeFlyPig() throws Exception {FlyPig flyPig = new FlyPig();flyPig.setColor("black");flyPig.setName("riemann");flyPig.setName("audi");// ObjectOutputStream 对象输出流,将 flyPig 对象存储到E盘的 flyPig.txt 文件中,完成对 flyPig 对象的序列化操作ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("d:/flypig.txt")));oos.writeObject(flyPig);System.out.println("FlyPig 对象序列化成功!");oos.close();}/*** 反序列化*/private static FlyPig deserializeFlyPig() throws Exception {ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("d:/flypig.txt")));FlyPig pig = (FlyPig) ois.readObject();System.out.println("FlyPig 对象反序列化成功!");return pig;}}

对上面的2个操作文件流的类的简单说明

ObjectOutputStream代表对象输出流:

它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

ObjectInputStream代表对象输入流:

它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

具体怎么看运行情况。

第一种:上来就这些代码,不动,直接run,看效果。

实际运行结果,他会在 d:/flyPig.txt 生成个文件。

FlyPig 对象序列化成功!
FlyPig 对象反序列化成功!
FlyPig{name='audi', color='black', car='null', AGE='269'}

从运行结果上看:

  1. 他实现了对象的序列化和反序列化。

  2. transient 修饰的属性,是不会被序列化的。我设置的奥迪四个圈的车不见啦,成了null。my god。

  3. 你先别着急说,这个静态变量AGE也被序列化啦。这个得另测。

第二种:为了验证这个静态的属性能不能被序列化和反序列化,可如下操作。

public class SerializableTest {public static void main(String[] args) throws Exception {serializeFlyPig();
//      FlyPig flyPig = deserializeFlyPig();
//      System.out.println(flyPig.toString());}

这个完了之后,意思也就是说,你先序列化个对象到文件了。这个对象是带静态变量的static。

现在修改flyPig类里面的AGE的值,给改成26吧。

然后,看下图里面的运行代码和执行结果。

public class SerializableTest {public static void main(String[] args) throws Exception {
//      serializeFlyPig();FlyPig flyPig = deserializeFlyPig();System.out.println(flyPig.toString());}

输出结果:

FlyPig 对象反序列化成功!
FlyPig{name='audi', color='black', car='null', AGE='26'}

可以看到,刚刚序列化的269,没有读出来。而是刚刚修改的26,如果可以的话,应该是覆盖这个26,是269才对。

所以,得出结论,这个静态static的属性,他不序列化。

第三种:示范这个 serialVersionUID 的作用和用法

最暴力的改法,直接把model的类实现的这个接口去掉。然后执行后面的序列化和反序列化的方法。直接报错。

抛异常:NotSerializableException

这个太暴力啦,不推荐这么干。

然后就是,还和上面的操作差不多,先是单独执行序列化方法。生成文件。然后,打开属性 addTip ,这之后,再次执行反序列化方法,看现象。

抛异常:InvalidClassException 详情如下。

InvalidClassException: com.lxk.model.FlyPig;
local class incompatible:
stream classdesc serialVersionUID = 7230772301104163489,
local class serialVersionUID = -2293195637094031536Exception in thread "main" java.io.InvalidClassException: com.test.FlyPig; local class incompatible: stream classdesc serialVersionUID = 7230772301104163489, local class serialVersionUID = -2293195637094031536at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:699)at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1885)at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1751)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2042)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1573)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:431)at com.test.SerializableTest.deserializeFlyPig(SerializableTest.java:33)at com.test.SerializableTest.main(SerializableTest.java:8)

解释一下:

因为我再model里面是没有明确的给这个 serialVersionUID 赋值,但是,Java会自动的给我赋值的,这个值跟这个model的属性相关计算出来的。

我保存的时候,也就是我序列化的时候,那时候还没有这个addTip属性呢,所以,自动生成的serialVersionUID 这个值,在我反序列化的时候Java自动生成的这个serialVersionUID值是不同的,他就抛异常啦。

(你还可以反过来,带ID去序列化,然后,没ID去反序列化。也是同样的问题。)

再来一次,就是先序列化,这个时候,把 private static final long serialVersionUID = 1L; 这行代码的注释打开。那个addTip属性先注释掉,序列化之后,再把这个属性打开,再反序列化。看看什么情况。

FlyPig 对象反序列化成功!
FlyPig{name='audi', color='black', car='null', AGE='26', addTip='null'}

这个时候,代码执行OK,一切正常。good。序列化的时候,是没的那个属性的,在发序列化的时候,对应的model多了个属性,但是,反序列化执行OK,没出异常。

这个现象对我们有什么意义:

老铁,这个意义比较大,首先,你要是不知道这个序列化是干啥的,万一他真的如开头所讲的那样存数据库啦,socket传输啦,rmi传输啦。虽然我也不知道这是干啥的。你就给model bean 实现了个这个接口,你没写这个 serialVersionUID 那么在后来扩展的时候,可能就会出现不认识旧数据的bug,那不就炸啦吗。回忆一下上面的这个出错情况。想想都可怕,这个锅谁来背?

所以,有这么个理论,就是在实现这个Serializable 接口的时候,一定要给这个 serialVersionUID 赋值,就是这么个问题。

这也就解释了,我们刚刚开始编码的时候,实现了这个接口之后,为啥IDEA编辑器要黄色警告,需要添加个这个ID的值。而且还是一长串你都不知道怎么来的数字。

下面解释这个 serialVersionUID 的值到底怎么设置才OK。

首先,你可以不用自己去赋值,Java会给你赋值,但是,这个就会出现上面的bug,很不安全,所以,还得自己手动的来。

那么,我该怎么赋值,eclipse可能会自动给你赋值个一长串数字。这个是没必要的。

可以简单的赋值个 1L,这就可以啦。。这样可以确保代码一致时反序列化成功。

不同的serialVersionUID的值,会影响到反序列化,也就是数据的读取,你写1L,注意L大些。计算机是不区分大小写的,但是,作为观众的我们,是要区分1和L的l,所以说,这个值,闲的没事不要乱动,不然一个版本升级,旧数据就不兼容了,你还不知道问题在哪。。。

第四种:当属性是对象的时候,没实现序列化接口

当属性是对象的时候,如果这个对象,没实现序列化接口,那么上面的方法在序列化的时候就在执行oos.writeObject(flyPig)时候,报错了“Exception in thread “main” java.io.NotSerializableException: com.lxk.model.Bird”。然后给刚刚的属性的对象加上实现序列化的接口之后,上面的测试就正常通过了。

下面是摘自 jdk api 文档里面关于接口 Serializable 的描述

类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。可序列化类的所有子类型本身都是可序列化的。因为实现接口也是间接的等同于继承。序列化接口没有方法或字段,仅用于标识可序列化的语义。

关于 serialVersionUID 的描述

序列化运行时使用一个称为 serialVersionUID 的版本号与每个可序列化类相关联,该序列号在反序列化过程中用于验证序列化对象的发送者和接收者是否为该对象加载了与序列化兼容的类。

如果接收者加载的该对象的类的 serialVersionUID 与对应的发送者的类的版本号不同,则反序列化将会导致 InvalidClassException。可序列化类可以通过声明名为 “serialVersionUID” 的字段(该字段必须是静态 (static)、最终 (final) 的 long 型字段)显式声明其自己的 serialVersionUID:

如果可序列化类未显式声明 serialVersionUID,则序列化运行时将基于该类的各个方面计算该类的默认 serialVersionUID 值,如“Java™ 对象序列化规范”中所述。

不过,强烈建议 所有可序列化类都显式声明 serialVersionUID 值,原因是计算默认的 serialVersionUID 对类的详细信息具有较高的敏感性,根据编译器实现的不同可能千差万别,这样在反序列化过程中可能会导致意外的 InvalidClassException。

因此,为保证 serialVersionUID 值跨不同 java 编译器实现的一致性,序列化类必须声明一个明确的 serialVersionUID 值。还强烈建议使用 private 修饰符显示声明 serialVersionUID(如果可能),原因是这种声明仅应用于直接声明类 – serialVersionUID 字段作为继承成员没有用处。数组类不能声明一个明确的 serialVersionUID,因此它们总是具有默认的计算值,但是数组类没有匹配 serialVersionUID 值的要求。

往期推荐

音效摸鱼还不够爽?试试IDE里打几盘魂斗罗?

3折购书优惠码限时抢,第二波书单来了!

面试:知道 CopyOnWriteArrayList 吗?

居然还有人在用 System.out.println打日志的吗?

不错的秒杀系统架构分析与实战!

一个让你敲代码的同时,找回童年乐趣的 IntelliJ 插件

扫一扫,关注我

一起学习,一起进步

每周赠书,福利不断

深度内容

推荐加入

最近热门内容回顾   #技术人系列

面试:什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决?...相关推荐

  1. 招银网络二面:什么是序列化?常见的序列化协议有哪些?

    今天分享一道读者面试招银网络科技遇到的面试真题. 下面是正文. 序列化和反序列化相关概念 什么是序列化?什么是反序列化? 如果我们需要持久化 Java 对象比如将 Java 对象保存在文件中,或者在网 ...

  2. java 序列化慢_java原生序列化慢在哪里?

    Java原生序列化和二进制序列化性能比较 序列化速度 package com.clq.netty.serializable; import java.io.ByteArrayOutputStream; ...

  3. java rest 序列化_http请求/restful/序列化反序列化/JSON

    1.为什么要实现Serializable (1)将内存中的对象数据存在磁盘中持久化 (2)将对象从一个应用程序发送到另一个应用程序 实现Serializable接口可以把对象序列化为字节流,实现传输必 ...

  4. Java之什么是序列化以及为什么要序列化

    http://blog.csdn.net/tlycherry/article/details/8986720  序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化,将数据分解成字 ...

  5. python建立数据库并序列化_Python之数据序列化(json、pickle、shelve)

    一.python类型数据和JSON数据格式互相转换 pthon 中str类型到JSON中转为unicode类型,None转为null,dict对应object 二. 序列化/反序列化 将对象转换为可通 ...

  6. Atitit php序列化 php的serialize序列化和json序列化

    Atitit php序列化 php的serialize序列化和json序列化 PHP 对不同类型的数据用不同的字母进行标示,Yahoo 开发网站提供的Using Serialized PHP with ...

  7. java序列化_技术干货 | JAVA反序列化漏洞

    目录 反序列化漏洞 序列化和反序列化 JAVA WEB中的序列化和反序列化 对象序列化和反序列范例 JAVA中执行系统命令 重写readObject()方法 Apache Commons Collec ...

  8. 对象序列化Java中的序列化

    首先声明,我是一个菜鸟.一下文章中出现技术误导情况盖不负责 当两个进程在停止远程通信时,彼此可以发送各种类型的数据.无论是何种类型的数据,都市以二进制序列的情势在络网上传送.发送方需要把这个Java对 ...

  9. 字定义JSON序列化支持datetime格式序列化

    字定义JSON序列化支持datetime格式序列化 由于json.dumps无法处理datetime日期,所以可以通过自定义处理器来做扩展,如: import json from datetime i ...

  10. php7序列化,PHP内核层解析反序列化漏洞

    前言 在学习PHP的过程中发现有些PHP特性的东西不好理解,如PHP中的00截断,MD5缺陷,反序列化绕过__wakeup等等.本人不想拘泥于表面现象的理解,想探究PHP内核到底是怎样做到的. 下面是 ...

最新文章

  1. 华为交换机STP端口角色状态
  2. pyspark 读取本txt 构建RDD
  3. php是范例,php基础范例
  4. Last Theorem CodeForces - 1325F(dfs树找最大环+思维)
  5. 微软 CEO 纳德拉痛失爱子
  6. odbc mysql导出access_将mysql数据导入access数据库
  7. “让天下没有难开的店”,宣言来自无人车公司AutoX
  8. python自动化办公教程百度云-用Python自动办公,做职场高手,16章完整版百度云盘...
  9. useCallback()和useMemo()的作用
  10. [再学Python] - 7 - 文件
  11. 苹果手机屏幕镜像搜索不到电视_康佳电视投屏不了,3个办法解决!
  12. 架构实战体会,结合《蔡学镛:架构的5个观察角度》
  13. UWB定位系统在冬奥会上满足哪些需求
  14. 需求与商业模式创新-4-战略
  15. 对日软件工程师的几种招聘要求
  16. 青龙脚本--联通领话费流量等
  17. 互动3D网站已触手可及
  18. 3. 天线设计理论 - 什么是带宽
  19. C# (1)点击菜单跳出新窗体,(2)在主窗体中切换子窗体(pannel控件的使用)
  20. 打怪小游戏 勇者打恶龙1.1

热门文章

  1. 为何多线程就能提高Java程序的执行效率
  2. linux docker 报错 Failed to get D-Bus connection: Operation not permitted
  3. python3 redis操作 错误 cannot import name 'StrictRedis' from 'redis'
  4. mysql 计算两个日期之间的天数
  5. linux 内核可装载模块 版本检查机制
  6. Android开发--多媒体应用开发(一)--MediaPlayer的使用介绍
  7. 交换分区的查看与扩容
  8. (五)OpenStack---M版---双节点搭建---Nova安装和配置
  9. Linux 段错误详解
  10. using bgp data to find spammers