zoukankan      html  css  js  c++  java
  • 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传输的数据格式
      序列化速度快的原因:编码 / 解码 方式简单(只需要简单的数学运算 = 位移等等),采用特殊方式标记,更加紧凑

    坚定信念,持之以恒
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    自动发现
    1.1 开启二进制日志
    mysql
    html常见的块元素和行内元素(特别注意个别块元素不能嵌套其他块元素)
    Linux查看物理CPU个数、核数、逻辑CPU个数
    查看linux服务器的系统信息
    谷歌浏览器无法输入中文——解决
    Linux下查看正在使用的端口
    入园第三天-记录
    Visual Studio2015 Community一些必备插件
  • 原文地址:https://www.cnblogs.com/zhouguanglin/p/14429031.html
Copyright © 2011-2022 走看看