Java --------- I/O(七) 序列化

一、啥是序列化

原先是  在开发中,经常需要将对象的信息保存到磁盘中便于以后检索,可以使用之前的方法逐一对对象的属性信息进行操作,这样做很繁琐,容易出错,尤其是在大型的项目中,为每一个对象编写代码,将字段和属性保存磁盘以及从磁盘还原这些字段和属性,更是难搞,而序列化提供了轻松实现这个目标的方法

在内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。从这个角度那么就可以理解为,将数据对象转换为二进制的流的过程称为对象的序列化(Serialization)。反之将二进制流恢复为数据对象的过程称为反序列化(Deserialization )。

维基上的你品品

大致过程

在序列化的过程中,会将对象的公有成员、私有成员包括类名转换为字节流,然后把字节流写入数据流,存储到存储介质中(如磁盘上的文件)而且保存了充分的信息以恢复数据对象,自身又很小

好处:
java 对象序列化后,可以将其转换为字节序列,这些字节序列可以被保存到磁盘上也可以借助网络进行传输,由于序列化的对象是一个二进制状态,这样就可以在不同的操作系统上通过反序列化得到相同的对象实现了平台无关性,自身还小,用处还大。

二、常见的序列化方式:  三种

one、java 原生序列化 :Java类通过实现Serializable 接口来实现该类对象的序列化,这个接口没有任何方法,只起到标识作用。

   优:保留了对象类的元数据(如类、成员变量、继承类信息等)、对象的数据等,而且兼容性也是最好的,

  缺:不支持跨语言,性能一般,

注意:

1、为啥实现的Serialization接口的时候要显示的定义serialVersionUID的属性值

  如果不设置每次运行时,编译器会根据类的内部实现,包括类名、接口名、方法和属性等来自动生成serialVersionUID,如果类源代码有修改,重新编译后serialVersionUID的取值可能会发生变化

2、修改serialVersionUID的值时要根据兼容性决定是否修改

  如果是兼容升级,不要修改serialVersionUID的值,避免反序列化失败

  如果是兼容不升级,需要修改serialVersionUID的值,避免反序列化混乱

3、java 反序列化时,不会调用类的无参构造方法,而是调用native 方法将成员变量赋值为对应类型的初始值。(大佬基于性能和兼容性考虑不建议使用java 原生序列化,但是我下文主要介绍这种方式,了解一下)

two、Hessian 序列化: 把复杂对象的所有属性存储在一个Map 中进行序列化

  Hessian  :一种支持动态类型、跨语言、基于对象传输的网络协议。

  优:1、可以被其他语言(如C++,Python)反序列化,支持脚本语言

    2、协议简单比Java原生序列化高效(序列化的二进制流大小是java 原生序列化的50%,序列化耗时的30%,反序列化耗时的20%)

    3、自描述序列化类型。不依赖外部描述文件或接口定义,用一个字节表示常用的基础类型,极大缩短二进制流

  缺:由于Hessian 会把复杂对象的所有属性存储在一个Map 中进行序列化。所以在父类、子类存在同名变量的情况下,会导致子类同名变量被父类的值覆盖(有没有品到序列化时时先序列化子类然后序列化父类)

  

Three、JSON 序列化 :将数据对象转换为JSON字符串

  JSON:(JavaScript Object Notation)一种轻量级的数据交换格式。

  优:可读性好,方便调试

  缺:序列化的过程中抛弃了类型信息,所以在反序列化时只有提供类型信息才能准确的反序列化

三、风险与防范

one、使用序列化的风险:

   由于序列化通常通过网络传输对象,而对象中往往有敏感数据,所以黑客常常以此作为攻击点,通过读取文件或拦截网络的方式来获取这些信息,利用反序列化的时候构造恶意代码。

  java 工程中广泛使用的Apache Commons Collections 、Jackson、fastjson 等都出现过反序列化的漏洞

two、防范方法:

  1、对象的敏感属性不进行序列化传输,使用transient 关键字,可以避免把此属性信息转换为序列化的二进制流

  2、使用对称与非对称加密方式独立传输,在使用某个方法把属性还原到对象中。

end,在使用序列化的时候一定呀有安全防范意识,对传入数据的内容进行校检或权限控制,及时更新安全漏洞,避免受到攻击。

四、操作

java 原生序列化,实现java.io.Serialzable 接口,Serializable 表示可串行的、可序列化的,也被称为串行化

  如:String 类、包装类、Date类等都实现了Serializable接口

one、序列化保存对象信息

步骤:

  1、将需要序列化的对象实现 Serializable接口

  2、创建一个对象输出流(ObjectOutputStream),里面包一个文件输出流(FileOutputStream)用于关联源文件

  3、通过对象输出流的writeObject()方法写对象,也就是在关联的源文件中输出可序列化对象

将需要序列化的对象实现 Serializable接口

 进行序列化操作

two、反序列化获取对象信息

反序列化:从特定存储介质中读取数据并重新构建成对象

步骤:

1、创建一个对象输入流(ObjectInputStream),将关联好源文件的fileInputStream 放到里面

2、通过对象输入流的 readObject() 方法读取对象,它的返回值是一个Object类型的,所以强转为原先的对象类型

多个对象的序列化和反序列化操作

当需要保存多个对象的时候,借助集合保存

 反序列化获取多个

 

 

注意:

  1、如果向文件中使用序列化机制写入多个对象,那么反序列化恢复对象的时候,必须按照写入的顺序读取。

  2、如果一个可序列化的类,有多个父类(包括直接或间接父类),则这些父类要么是可序列化的,要么有无参数的构造器;否则会抛出异常。

 

序列化的算法规则:

  1、所有保存到磁盘中的对象都有一个序列号

  2、当程序试图序列化一个对象时,将会检查是否已经被序列化,只有序列化后的对象,才能被转换成字节序列输出

  3、如果对象已经被序列化,则程序直接输出一个序列化编号,而不再重新序列化。

猜你喜欢

转载自www.cnblogs.com/obge/p/12907587.html