Java序列化的相关认知

序列化相关认识

序列化的意义


​ 将内存中的对象,以字节码形式存储起来,等待用时反序列化回来,主要是IO操作,可以有更长的生命周期和跨项目性质的作用。

序列化和反序列化


序列化:将对象的信息转化为字节码的形式存储或传输的过程,称为序列化。

public class Person{
  private String name;
  
 	public void setName(String name){
    this.name = name
  }
  
  public String getName(){
    return name;
  }
}
Person person = new Person();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("person.txt")));
oos.writeObject(person);

反序列化:序列化的逆向过程,将字节码读取转化为对象的过程,称为反序列化。

ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("person.txt")));
Person person = (Person) ois.readObject();
如何实现序列化
  1. 在java中只要一个类实现了java.io.Serializable 接口,那么它就可以被序列化
  2. 基于JDK序列化方式实现
    JDK提供了Java对象的序列化方式,主要通过输出流 java.io.ObjectOutputStream 和 对象输入流 java.io.ObjectInputStream 来实现。其中,被序列化的对象需要 实现java.io.Serializable接口

序列化的问题


序列化的实现方式优劣指标

  • 序列化后的数据大小
  • 序列化操作的速度以及系统开销

Java序列化的缺点

  • 序列化后的数据比较大
  • 其他语言无法识别和对接

深入理解序列化


JDK序列化中serialVersionUID的作用

  • Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的 serialVersionUID与本地相应实体类的 serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是 InvalidCastException

  • 如果没有为指定的 class 配置 serialVersionUID,那么 java 编译器会自动给这个 class 进行一个摘要算法,类似于指纹算法,只要这个文件有任何改动,得到的 UID 就会截然不同的,可以保证在这么多类中,这个编号是唯一的

serialVersionUID生成规则

  1. 默认的1L,比如:private static final long serialVersionUID = 1L;
  2. 根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段
    • 当 实 现 java.io.Serializable 接 口 的 类 没 有 显 式 地 定 义 一 个serialVersionUID 变量时候,Java 序列化机制会根据编译的 Class 自动
    • 生成一个 serialVersionUID 作序列化版本比较用,这种情况下,如果Class 文件(类名,方法明等)没有发生变化(增加空格,换行,增加注释等等),就算再编译多次,serialVersionUID 也不会变化的。

静态变量序列化

​ 序列化时并不保存静态变量,序列化保存的是对象的状态,静态变量属于类的状态,因此序列化并不保存静态变量。

父类序列化问题

​ 子类实现了Serializable接口,父类未实现,在子类中调用父类属性赋值,序列化过程中出现的情况

  • 若父类未实现Serializable,在反序列化中,无法获取赋值给父类属性值。
  • 父类实现Serializable,则子类无需再次实现Serializable接口,自动实现序列化
  • 如果一个可序列化的类的成员不是基本类型,也不是String类型,那这个引用类型也必须是可序列化的;否则,会导致此类不能序列化

Transient关键字

使用transient修饰的属性,java序列化时,会忽略掉此字段,所以反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false,初始值。

重复序列化问题

Java序列化同一对象,并不会将此对象序列化多次得到多个对象

Java序列化算法

  • 所有保存到磁盘的对象都有一个序列化编码号
  • 当程序试图序列化一个对象时,会先检查此对象是否已经序列化过,只有此对象从未(在此虚拟机)被序列化过,才会将此对象序列化为字节序列输出。
  • 如果此对象已经序列化过,则直接输出编号即可。当写入文件为同一对象时,并不会再将对象的内容进行存储,而只是再次存储一份引用,上面增加的5字节的存储空间就是新增 引用和一些控制信息的空间。反序列化时,恢复引用关系,该存储规则极大的节省了存储空间。

可以作为深度克隆

​ Java的拷贝一般都是实现Cloneable接口,并重写clone方法进行拷贝操作,主要是对象表皮复制了一份,内部属性的对象引用未改变。

属性类型是基本数据类型或者String字符串类型不影响。

​ 针对拷贝时对象属性引用问题有几种解决方式

  1. 可以在对象属性字段的对象也加上实现Cloneable接口调用clone(比较麻烦),如下:

     @Override
        public Object clone() throws CloneNotSupportedException {
            Teacher teacher = (Teacher) super.clone();
            //把引用类型的变量在克隆一份
            teacher.setStudent((Student) teacher.getStudent().clone());
            return teacher;
        }
    
    
  2. 可以利用序列化的方式,将对象转化为字节流,再转化回对象,做个中转(可能稍微有点麻烦,但非常实用),如下:

        public Object copyObject(Object object) throws IOException, ClassNotFoundException {
            //创建字节数组输出流将索要拷贝对象写入
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            //创建对象输出流将字节数组输出流传入直接将对象写入字节输出流
            ObjectOutputStream objectOutputStrea = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStrea.writeObject(object);
            //将刚写入的输出流转化为字节数组传入字节数组输入流
            ByteArrayInputStream byteArrayInputStream = new  ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            //对象输入流包装读取为一个对象
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return objectInputStream.readObject();
        }
    
  3. 还有可以使用JSON的方式,先将对象转为json,再转回对象,这是不错的想法

常见的序列化技术

  • Java JDK序列化:简单方便,但是不能跨语言,以及数据量大
  • XML序列化:可能是通用、阅读好吧(个人不喜欢),缺点比较明显,效率低,数据大
  • JSON序列化:比较轻量级,数据量小,阅读性高,比较通用(个人比较推荐),主要有(Jackson、FastJson、GSON等)
  • Hessian 序列化框架:Hessian是一个支持跨语言传输的二进制序列化协议,相对于Java默认的序列化机制来说,Hessian具有更好的性能和易用性,而且支持对重不同的语言,实际上Dubbo采用的就是Hessian序列化来实现,只不过Dubbo对Hessian进行重构,性能更高
  • Protobuf 序列化框架:个人认为这个和JSON没啥太大差别,只是比JSON更加浓缩,数据格式简洁、量少、但是对对象的支持不太好,若是有解析框架都好说,主要注重RPC传输的数据格式
    序列化速度快的原因:编码 / 解码 方式简单(只需要简单的数学运算 = 位移等等),采用特殊方式标记,更加紧凑

坚定信念,持之以恒
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
原文地址:https://www.cnblogs.com/zhouguanglin/p/14429031.html