zoukankan      html  css  js  c++  java
  • 序列化与反序列化

    序列化的意义

      在考虑系统性能的时候,会考虑序列化。远程通信的时候,就要考虑序列化。序列化和反序列化是每天都会碰到的问题。就我而言,序列化这个概念基本上一直在听到,但是很少有了解。Java对象的传输、分布式架构、大数据量的工程。
      Java 平台允许我们在内存中创建可复用的Java 对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,所以这些对象的生命周期不会比JVM 的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在将来重新读取被保存的对象。Java 对象序列化就能够帮助我们实现该功能。
      序列化是把对象的状态信息转化为可存储或传输的形式过程,也就是把对象转化为字节序列的过程称为对象的序列化。
      反序列化是序列化的逆向过程,把字节数组反序列化为对象,把字节序列恢复为对象的过程成为对象的反序列化。
      那么如何去评价一个序列化工具呢?
      评价一个序列化算法优劣的两个重要指标是:序列化以后的数据大小;序列化操作本身的速度及系统资源开销(CPU、内存)。


    常见的序列化工具

    Java本身自带的序列化

      Java本身就有一个序列化工具(实现Serializable接口),但是缺点也是存在的:(1)序列化后数据比较大。(2)其他语言无法识别
      Java本身序列化方法:
      @Override
        public <T> byte[] serializer(T obj) {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = null;
            try {
                objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
                objectOutputStream.writeObject(obj);
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if(objectOutputStream != null) {
                    try {
                        objectOutputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return byteArrayIutputStream.toByteArray();
        }
    
        @Override
        public <T> T deSerializer(byte[] bytes, Class<T> clazz) {
            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            ObjectInput objectInput = null;
            try {
                objectInput = new ObjectInputStream(byteArrayInputStream);
                return (T)objectInput.readObject();
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

      对于一个希望通过序列化的类,可能会报InvalidClassException,这是因为序列化的类最好加上serialVersionUID,用来让系统判断序列化的可靠性。

    serialVersionUID 的作用
      Java 的序列化机制是通过判断类的serialVersionUID 来验证版本一致性的。在进行反序列化时,JVM 会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID 进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException如果没有为指定的class 配置serialVersionUID,那么java 编译器会自动给这个class 进行一个摘要算法,类似于指纹算法,只要这个文件有任何改动,得到的UID 就会截然不同的,可以保证在这么多类中,这个编号是唯一的。

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

      Java对象序列化过程中,如果序列化得到的对象增加或者减少一个变量,并不会报错,仅仅是某个字段读取不到。对于一个静态变量的序列化,静态变量不会参与序列化。因为序列化保存的是一个对象的状态,而静态变量属于一个类的状态。

    transient

      transient修饰的字段表示不会在序列化过程被保存,他的值在反序列化之后仍是类定义的值。也可以手动写到流里面,来绕过序列化

    序列化与继承

      如果说一个子类实现了序列化,父类没有实现序列化,在子类被反序列化之后,是无法获得父类的值,即子类继承父类已经被定义的那个值是空的。

      如果一个父类实现序列化,那么子类自动实现序列化,不用继承Serializable接口。

      对于同一个对象写入流两次,流里的数据不会加倍,而是增加五个字节(增加新增引用和一些控制信息的空间),因为当流里存在同一个对象的时候,只是会增加一个引用。这算是个优点,节省了存储空间。

    序列化实现克隆

      Java对于每个接口类都具有克隆能力,但只是浅克隆。浅克隆只是新建对象,对原对象的一些变量只是复制它的引用。我实例2 克隆实例1,当实例2改变某个值后,实例1也会改变。深克隆实现Serializable接口,把对象序列化流中,再从流里读出来,这个对象就不是原来的对象了,所有的变量的引用都会新建一个引用。

    xml序列化框架

      优点是可读性强,缺点是序列化之后的数据比较大。在技术要求比较高的时候,一般不会用到它。

      代码实例:

    public class XmlSerializer  {
    
        XStream xStream = new XStream(new DomDriver());
        
        public <T> String serializer(T obj) {
            return xStream.toXML(obj);
        }
    
    
        public <T> T deSerializer(String bytes, Class<T> clazz) {
            return (T)xStream.fromXML(bytes);
        }
    
        public static void main(String[] args) {
            XmlSerializer iSerializer = new XmlSerializer();
            User user = new User();
            
            String bytes = iSerializer.serializer(user);
            user.setName("jolivan");
            System.out.println(new String(bytes));
            
            User userNow = iSerializer.deSerializer(bytes,User.class);
            System.out.println(userNow.getName());
            System.out.println(userNow.getAge());
        }
    }
    ==============输出==================
    <serial.User>
      <name>Lushe</name>
      <age>23</age>
    </serial.User>
    Lushe
    23

      我们可以看到,根据数据我们就知道他是个什么类,里面有啥。可读性非常高。

    JSON

      JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,相对于XML 来说,JSON 的字节流更小,而且可读性也非常好。现在JSON数据格式在企业运用是最普遍的
      JSON 序列化常用的开源工具有很多
      1. Jackson (https://github.com/FasterXML/jackson)
      2. 阿里开源的FastJson (https://github.com/alibaba/fastjon)
      3. Google 的GSON (https://github.com/google/gson)
      这几种json序列化工具中,Jackson 与fastjson 要比GSON 的性能要好,但是Jackson、GSON 的稳定性要比Fastjson 好。而fastjson的优势在于提供的api 非常容易使用。

      用阿里的FastJson来示例一下:

    public class FastjsonSerializeer implements ISerializer {
    
    
        @Override
        public <T> byte[] serializer(T obj) {
            return JSON.toJSONBytes(obj);
        }
    
        @Override
        public <T> T deSerializer(byte[] bytes, Class<T> clazz) {
            return (T)JSON.parseObject(bytes,clazz);
        }
    
    
        public static void main(String[] args) {
            ISerializer iSerializer = new FastjsonSerializeer();
            User user = new User();
    
            byte [] bytes = iSerializer.serializer(user);
            user.setName("jolivan");
            System.out.println(new String(bytes));
    
            User userNow = iSerializer.deSerializer(bytes,User.class);
            System.out.println(userNow.getName());
        }
    }

    ===================输出=====================

    {"age":"23","name":"Lushe"}
    Lushe

     

    hessian

      dubbo里使用的就是它,但对它做了优化,又称为hessian2。

    Protobuf

      优势:(1)独立语言(可以基于不同的语言)、独立平台(跨平台交互)(2)性能高,压缩性好;(3)解析性好

      缺陷:实现起来很麻烦,学习成本大。

      它有独立的编译器

    序列化的实际应用举例

      比如说我们在一个分布式系统中,有一个订单模块和一个支付模块,订单模块基于一个协议(dubbo)调用订单系统,底层传输的事二进制数据,那么我们要做的事情就是把对象转化为一个二进制数据,这就是序列化的场景。

  • 相关阅读:
    爬虫笔记(四)------关于BeautifulSoup4解析器与编码
    sublime_text_2 ubuntu下无法输入中文 解决方法
    PHP 随手记
    PHP与apache环境配置
    5分钟学会如何创建spring boot项目
    Java 解压zip压缩包
    利用JavaScript来实现用动态检验密码强度
    金融行业是如何丢失1分钱的
    Java多线程的三种实现方式
    教你如何快速定制 SpringBoot banner
  • 原文地址:https://www.cnblogs.com/Jolivan/p/9286524.html
Copyright © 2011-2022 走看看