1、什么是序列化:

两个服务之间要传输一个数据对象,就需要将对象转换成二进制流,通过网络传输到对方服务,再转换成对象,供服务方法调用。这个编码和解码的过程称之为序列化和反序列化。所以序列化就是把 Java 对象变成二进制形式,本质上就是一个byte[]数组。将对象序列化之后,就可以写入磁盘进行保存或者通过网络中输出给远程服务了。反之,反序列化可以从网络或者磁盘中读取的字节数组,反序列化成对象,在程序中使用。

2、序列化优点:

① 永久性保存对象:将对象转为字节流存储到硬盘上,即使 JVM 停机,字节流还会在硬盘上等待,等待下一次 JVM 启动时,反序列化为原来的对象,并且序列化的二进制序列能够减少存储空间

② 方便网络传输:序列化成字节流形式的对象可以方便网络传输(二进制形式),节约网络带宽

③ 通过序列化可以在进程间传递对象

3、序列化的几种方式:

参考文章:https://www.jianshu.com/p/7298f0c559dc

3.1、Java 原生序列化:

Java 默认通过 Serializable 接口实现序列化,只要实现了该接口,该类就会自动实现序列化与反序列化,该接口没有任何方法,只起标识作用。Java序列化保留了对象类的元数据(如类、成员变量、继承类信息等),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。

实现 Serializable 接口的类在每次运行时,编译器会根据类的内部实现,包括类名、接口名、方法和属性等自动生成一个 serialVersionUID,serialVersionUID 主要用于验证对象在反序列化过程中,序列化对象是否加载了与序列化兼容的类,如果是具有相同类名的不同版本号的类,在反序列化中是无法获取对象的,显式地定义 serialVersionUID 有两种用途:

  • 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的 serialVersionUID;
  • 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的 serialVersionUID;

如果源码改变,那么重新编译后的 serialVersionUID 可能会发生变化,因此建议一定要显示定义 serialVersionUID 的属性值。

3.2、Hessian 序列化:

Hessian 序列化是一种支持动态类型、跨语言、基于对象传输的网络协议。Java 对象序列化的二进制流可以被其他语言反序列化。 Hessian 协议具有如下特性:

  • 自描述序列化类型。不依赖外部描述文件或接口定义, 用一个字节表示常用
  • 基础类型,极大缩短二进制流
  • 语言无关,支持脚本语言
  • 协议简单,比 Java 原生序列化高效

Hessian 2.0 中序列化二进制流大小是 Java 序列化的 50%,序列化耗时是 Java 序列化的 30%,反序列化耗时是 Java 反序列化的20% 。

Hessian 会把复杂对象所有属性存储在一个 Map 中进行序列化。所以在父类、子类存在同名成员变量的情况下, Hessian 序列化时,先序列化子类 ,然后序列化父类,因此反序列化结果会导致子类同名成员变量被父类的值覆盖。

3.3、Json 序列化:

JSON 是一种轻量级的数据交换格式。JSON 序列化就是将数据对象转换为 JSON 字符串,在序列化过程中抛弃了类型信息,所以反序列化时需要提供类型信息才能准确地反序列化,相比前两种方式,JSON 可读性比较好,方便调试。

4、为什么不建议使用Java序列化

该部分主要参考文章:为什么我不建议你使用Java序列化 - 掘金

目前主流框架很少使用到 Java 序列化,比如 SpringCloud 使用的 Json 序列化,Dubbo 虽然兼容 Java 序列化,但默认使用的是 Hessian 序列化。这是为什么呢?主要是因为 JDK 默认的序列化方式存在以下一些缺陷:无法跨语言、易被攻击、序列化的流太大、序列化性能太差等

4.1、无法跨语言:

Java 序列化只支持 Java 语言实现的框架,其它语言大部分都没有使用 Java 的序列化框架,也没有实现 Java 序列化这套协议,因此,两个不同语言编写的应用程序之间通信,无法使用 Java 序列化实现应用服务间传输对象的序列化和反序列化。

4.2、易被攻击:

对象是通过在 ObjectInputStream 上调用 readObject() 方法进行反序列化的,它可以将类路径上几乎所有实现了 Serializable 接口的对象都实例化。这意味着,在反序列化字节流的过程中,该方法可以执行任意类型的代码,这是非常危险的。

对于需要长时间进行反序列化的对象,不需要执行任何代码,也可以发起一次攻击。攻击者可以创建循环对象链,然后将序列化后的对象传输到程序中反序列化,这种情况会导致 hashCode 方法被调用次数呈次方爆发式增长, 从而引发栈溢出异常。

序列化通常会通过网络传输对象,而对象中往往有敏感数据,所以序列化常常成为黑客的攻击点,攻击者巧妙地利用反序列化过程构造恶意代码,使得程序在反序列化的过程中执行任意代码。 Java 工程中广泛使用的 Apache Commons Collections、Jackson、fastjson 等都出现过反序列化漏洞。如何防范这种黑客攻击呢?有些对象的敏感属性不需要进行序列化传输,可以加 transient 关键字,避免把此属性信息转化为序列化的二进制流,除此之外,声明为 static 类型的成员变量也不能要序列化。如果一定要传递对象的敏感属性,可以使用对称与非对称加密方式独立传输,再使用某个方法把属性还原到对象中。

4.3、序列化后的流太大

序列化后的二进制流大小能体现序列化的性能。序列化后的二进制数组越大,占用的存储空间就越多,存储硬件的成本就越高。如果我们是进行网络传输,则占用的带宽就更多,这时就会影响到系统的吞吐量。

Java 序列化中使用了 ObjectOutputStream 来实现对象转二进制编码,那么这种序列化机制实现的二进制编码完成的二进制数组大小,相比于 NIO 中的 ByteBuffer 实现的二进制编码完成的数组大小,有没有区别呢?

我们可以通过一个简单的例子来验证下:

User user = new User();
user.setUserName("test");
user.setPassword("test");ByteArrayOutputStream os =new ByteArrayOutputStream();
ObjectOutputStream out = new ObjectOutputStream(os);
out.writeObject(user);
byte[] testByte = os.toByteArray();
System.out.print("ObjectOutputStream 字节编码长度:" + testByte.length + "\n");
ByteBuffer byteBuffer = ByteBuffer.allocate(2048);byte[] userName = user.getUserName().getBytes();
byte[] password = user.getPassword().getBytes();
byteBuffer.putInt(userName.length);
byteBuffer.put(userName);
byteBuffer.putInt(password.length);
byteBuffer.put(password);
byteBuffer.flip();
byte[] bytes = new byte[byteBuffer.remaining()];
System.out.print("ByteBuffer 字节编码长度:" + bytes.length+ "\n");

运行结果:

ObjectOutputStream 字节编码长度:99
ByteBuffer 字节编码长度:16

4.4、序列化性能太差:

序列化的速度也是体现序列化性能的重要指标,如果序列化的速度慢,网络通信效率就低,从而增加系统的响应时间。我们再来通过上面这个例子,来对比下 Java 序列化与 NIO 中的 ByteBuffer 编码的性能:

    User user = new User();user.setUserName("test");user.setPassword("test");long startTime = System.currentTimeMillis();for(int i=0; i<1000; i++) {ByteArrayOutputStream os =new ByteArrayOutputStream();ObjectOutputStream out = new ObjectOutputStream(os);out.writeObject(user);out.flush();out.close();byte[] testByte = os.toByteArray();os.close();}long endTime = System.currentTimeMillis();System.out.print("ObjectOutputStream 序列化时间:" + (endTime - startTime) + "\n");
long startTime1 = System.currentTimeMillis();
for(int i=0; i<1000; i++) {ByteBuffer byteBuffer = ByteBuffer.allocate( 2048);byte[] userName = user.getUserName().getBytes();byte[] password = user.getPassword().getBytes();byteBuffer.putInt(userName.length);byteBuffer.put(userName);byteBuffer.putInt(password.length);byteBuffer.put(password);byteBuffer.flip();byte[] bytes = new byte[byteBuffer.remaining()];
}
long endTime1 = System.currentTimeMillis();
System.out.print("ByteBuffer 序列化时间:" + (endTime1 - startTime1)+ "\n");

运行结果:

ObjectOutputStream 序列化时间:29
ByteBuffer 序列化时间:6

Java IO篇:序列化与反序列化相关推荐

  1. fileinputstream自定义类序列化和反序列化_Rest Assured篇:Java中的序列化和反序列化...

    点击上方蓝字设为星标 每天傍晚伴你一起成长! Java 中的序列化和反序列化是一个重要的编程概念.它适用于所有主要的编程语言.在本章中,我们将尝试在Java语言的上下文中理解此概念.在本章的最后,我们 ...

  2. Java对象的序列化与反序列化

    序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如文件或是内存缓冲区等.在网络传输过程中,可以是字节或是 ...

  3. java对象的序列化和反序列化详细解释

    java对象的序列化和反序列化是什么意思 1.序列化是干啥用的? 序列化的原本意图是希望对一个Java对象作一下"变换",变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结 ...

  4. 反序列化对象列表发生异常_面试官:你知道Java对象的序列化与反序列化背后的原理吗?...

    序列化与反序列化 序列化 (Serialization)是将对象的状态信息转换为可以存储或传输的形式的过程.一般将一个对象存储至一个储存媒介,例如档案或是记亿体缓冲等.在网络传输过程中,可以是字节或是 ...

  5. Java对象的序列化和反序列化

    [感谢]孤傲苍狼的 Java基础学习总结--Java对象的序列化和反序列化 一.序列化和反序列化的概念 序列化就是一种用来处理对象流的机制,所谓对象流也就是将对象的内容进行流化.可以对流化后的对象进行 ...

  6. java.io.Serializable 序列化问题【原】

    java.io.Serializable 序列化问题 Person.java package a.b.c;public class Person implements java.io.Serializ ...

  7. JAVA中JSON序列化和反序列化

    JAVA中JSON序列化和反序列化 1 FASTJSON的序列化和反序列化 1.1 主函数 1.2 自定义对象 2 GSON序列化和反序列化 2.1 主函数 2.2 自定义对象 总结 不同包序列化和反 ...

  8. 一文带你全面了解java对象的序列化和反序列化

    本文分享自华为云社区<java中什么是序列化和反序列化?>,原文作者:dayu_dls . 这篇文章主要给大家介绍了关于java中对象的序列化与反序列化的相关内容,文中通过详细示例代码介绍 ...

  9. K:java中的序列化与反序列化

    Java序列化与反序列化是什么?为什么需要序列化与反序列化?如何实现Java序列化与反序列化?以下内容将围绕这些问题进行展开讨论. Java序列化与反序列化 简单来说Java序列化是指把Java对象转 ...

最新文章

  1. 股票移动平均线matlab,股票的移动平均线 (图文)
  2. 关于ark取得进程的镜像文件路径
  3. 在看完《Programming in Lua》之后
  4. 有赞客户行为收集与实时处理系统设计
  5. zookeeper源码分析之五服务端(集群leader)处理请求流程
  6. 详解京东商城智能对话系统(生成+检索)
  7. 服务的默认端口_Informatica端口管理
  8. IDEA新特性:提前知道代码怎么走!
  9. 电脑断网分析(故障排查手册)- 自救篇
  10. mysql更换主键遇到的一个问题
  11. Luogu P1963 [NOI2009]变换序列(二分图匹配)
  12. 合取范式 (CNF)
  13. Linux 修改hosts文件
  14. AES链路弹性和故障转移
  15. 一篇论文摘要计算机英语,计算机毕业论文英文摘要的写作方法.doc
  16. 如何实现百度mapv开源库与ol3的结合
  17. 前端实例1——blog页面(css样式)
  18. 【RS-Attack】Data Poisoning Attacks to Deep Learning Based Recommender Systems NDSS‘21
  19. 当你想通了,工作就顺了
  20. python更新pip后报异常

热门文章

  1. numpy.ndarray size changed, may indicate binary incompatibility. Expected 88 from C header, got 80 f
  2. 严选前端全栈工程师学习笔记
  3. 直播 | 天津大学副教授张长青:多模态融合的基础问题及算法研究
  4. 从变分编码、信息瓶颈到正态分布:论遗忘的重要性
  5. 三味Capsule:矩阵Capsule与EM路由
  6. Codeforces D. Fair 多源BFS求最短路
  7. 02 | 高性能 IO 模型:为什么单线程 Redis 能那么快?
  8. jvisualvm安装Visual GC插件
  9. 第一阶段:Java基础之异常和处理
  10. 「数据库系列杂谈」数据库访问性能优化