• Serializable 接口与 Java 序列化与反序列化


    0. 序列化的意义

    从内存到本地即为本地化或者在网络中进行传输,或叫序列化,持久化。

    某 Java 类实现 Serializable 接口的目的是为了可持久化(简单理解为本地化),比如网络传输或本地存储,为系统的分布式运行和异构部署提供先决支持条件。若没有序列化,我们熟悉的远程调用(RPC,无法读取远程主机内存中的任何目标,必须首选在远程将目标序列化),对象数据库都不可能存在。

    1. 一个简单 demo

    Serializable 为空接口,只是为了起到标识说明之义。

    • Person 类实现 Serializable 接口,这是一个简单的 JavaBean,实现了 Serializable 接口,可以在网络上传输,也可以本地存储然后读取。我们可以通过 Java 消息服务(Java Message Service)方式传递该对象(即通过网络传递一个对象),定义在消息队列中的数据类型为 ObjectMessage。

      public class Person implements Serializable {
          private String name;    // 省略 getter、setter
      }
    • 序列化及反序列化客户端代码:

      public class Producer
      {
          final static String FILE_NAME = "C:/obj.bin";
          public static void main(String[] args)
          {
              Person person = new Person();
              person.setName("");
              SerializationUtils.writeObject(person, FILE_NAME);
          }
      }
      public class Conducer
      {
          final static String FILE_NAME = "C:/obj.bin";
          public static void main(String[] args)
          {
              Person p = (Person)SerializationUtils.readObject(FILE_NAME);
              p.getName();
          }
      }
    • 其中的 SerializationUtils 工具类如下:

      public class SerializationUtils
      {
          // 序列化
          public static void writeObject(Serializable s, String filename)
          {
              try
              {
                  ObjectOutputStream oos = new ObjectOutputStream(
                          new FileOutputStream(filename));
                  oos.writeObject(s);
                  oos.close();
              } catch (Exception e)
              {
                  e.printStackTrace();
              }
          }
          // 反序列化
          public static Object readObject(String filename)
          {
              Object obj = null;
              try
              {
                  ObjectInputStream ois = new ObjectInputStream(
                          new FileInputStream(filename));
                  ois.readObject();
                  ois.close();
              } catch (Exception e)
              {
                  e.printStackTrace();
              }
              return obj;
          }
      }

    2. UID 的必要性

    正因为分布和异构,无论是读取本地文件还是远程调用,本地是无法在编译期获取待获取的类的完整信息的(成员变量和函数)。也即有可能发生,消息的生产者和消息的消费者所参考的类(Person)有差异,比如消息生产者的 Person 类添加一个年龄字段,而相应的消费者没有增加该属性。

    这种序列化和反序列化的类不一致的情形下,反序列时有可能会报一个 InvalidClassException 异常。原因就在于序列化和反序列化所对应的类版本发生了变化,JVM 不能把数据流转换为实例对象。那么,JVM 是根据什么来判断一个类版本的呢?

    通过 SerialVersionUID,也叫流标志符(Stream Unique Identifier)

    public class Person implements Serializable {
        private static final long serialVersionUID = 55799L;
    }

    此时如果发生如下形式的不一致的情况,生产者增加年龄属性,消费端 Person 还保持以前的版本。此时,虽然物理上,生产者和消费者对应的类版本不同,但是显式声明的 serialVersionUID 相同,反序列化也可以运行,所带来的业务问题即是消费端无法读取新增的业务属性。

  • 相关阅读:
    学PHP应注意的问题与知识点
    php 的生命周期
    Pyhton中汉字的使用方法(转)
    院外培训:GIS数据处理与建模高级培训班学习心得体会 来自
    绕人的python汉字问题
    arcmap导出或者打印时插入的图片和对象绘制失败
    【百度地图API】如何获取行政区域的边界? (转)
    VS2010不能编译.Net3.5项目的解决方法(转)
    ArcGIS中Python汉字使用说明(转)
    ArcGIS10联网无法启动问题解决
  • 原文地址:https://www.cnblogs.com/mtcnn/p/9421167.html
Copyright © 2020-2023  润新知