序列化的一些注意事项及建议

简介: 序列化的一些注意事项及建议

本文来自《改善java的151个建议》

 

建议11:养成良好习惯,显示声明UID

 image.png

我们先写一个序列化与反序列化的工具类SerilizationUtils

public class SerializationUtils {
  private static String FILE_NAME="E:/serializable.txt";
  public static void writeObject(Serializable s){
    try{
      ObjectOutputStream oob = new ObjectOutputStream(new FileOutputStream(FILE_NAME));
      oob.writeObject(s);
      oob.close();
    }catch(Exception e){};
  }
  public static Object readObject(){
    Object obj=null;
    try{
      ObjectInputStream oob = new ObjectInputStream(new FileInputStream(FILE_NAME));
      obj = oob.readObject();
      oob.close();
    }catch(Exception e){};
    return obj;
  }
}

Person类

public class Person implements Serializable  {
  /**
   * 这里先不显示的声明UID
   */
  //private static final long serialVersionUID = 1L;
  private String name;
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  //
  /*private String sex;
  public String getSex() {
    return sex;
  }
  public void setSex(String sex) {
    this.sex = sex;
  }*/
}

首先定义一个消息的产生者Produce

public class Produce {
  public static void main(String[] args){
    Person person = new Person();
    person.setName("****");
    //增加sex后
    //person.setSex("男");
    //序列化 保存到磁盘上
    SerializationUtils.writeObject(person);
    System.out.println(person);
  }
}

这里执行了之后  person就被序列化到了E:/serializable.txt

 

然后定义一个消费者

public class Consumer {
  public static void main(String[] args) throws Exception{
    //反序列化
    Person p = (Person)SerializationUtils.readObject();
    System.out.println("反序列化得到的值"+p.getName());
    //System.out.println("反序列化得到的值"+p.getSex());
  }
}

好 运行到这里是没有什么问题的;但是如果我们序列化与反序列化的时候参照的不是一个Person  会出现什么情况  ;


比如我们在序列化之前person还是只有一个属性name   执行produce (序列化)之后;给person增加一个sex属性(注意不要再运行produce (序列化)了);


增加了属性之后   我们运行consumer(反序列化);会出现一个错误

image.png

书上说的是InvalidClassException错误;但是我亲自执行报的是上面的错误;


为什么会这样呢?


原因是序列化与反序列化对应的类(person)版本不一致;JVM不能把数据流转换为实例对象;


那JVM是怎么判断一个类的对应版本呢?


是通过SerivalVersionUID ,也就流标识符,即类的版本定义

private static final long serialVersionUID = 1L;

UID可以隐式声明和显示声明;隐式声明是编译器自动生成  基本上市唯一的;如果改变了类 ; 它是UID也会改变


在反序列化的时候 jvm会比较数据流中的UID与当前类(person)是否一致;如果一致说明类没有改动;不一致说明是改动了,这是一个很好的效验机制;


但是;有特殊情况;例如:我的类改变不大,我希望在反序列化的时候也能把它序列化出来。那怎么办呢?


既然是判断UID是否一致,那我们让他们的UID是一致的就可以了,显示声明UID 可以很好的解决这一问题;


建议12:避免用序列化类在构造函数中未不变量final赋值

private static final long serialVersionUID = 1L;
  //final 常量是一个直接量   在反序列化的时候就会重新计算  保持final对象的新旧统一  有利于代码业务逻辑统一
  public final String testFinal="序列化之前";
//  public final String testFinal="序列化之后";

序列化的时候给testFinal赋值是   序列化之前          

 

序列化完成之后   将testFinal改成    序列化之后  

然后执行反序列化

  //反序列化
    Person p = (Person)SerializationUtils.readObject();
    System.out.println("反序列化得到的值:"+p.testFinal);

输出来的值是   序列化之前还是之后呢?


输出结果是:反序列化得到的值:序列化之后


为什么呢?我们在序列化的时候 将testFinal序列化成了数据流存在了磁盘中  按理说反序列化的时候  得到的也是  序列化之前的值啊    为什么变成了序列化之后?


这是因为序列化的基本规则之一:保持final新旧对象的相同。有利于代码业务逻辑的统一;也就是说,如果final是一个直接量  则反序列化时候final就会被重新计算;


final变量的另外一种赋值:构造函数赋值

public final String testFinal;
  public Person() {
    // TODO Auto-generated constructor stub
    testFinal="构造函数赋值  之前";
  }

序列化之后修改

testFinal="构造函数赋值  之后";

然后进行反序列化;猜想:这里输出应该会是   构造函数赋值  之后   吧?  因为规则一也是这样的啊!

 

那到底输出什么呢?

反序列化得到的值:构造函数赋值  之前

为什么还是之前  而不是改变之后的呢?  原因是另一个规则

 

反序列化时构造函数不被执行!

 

建议13:避免为final变量复杂赋值

image.png

image.png

image.png

总结:反序列化在以下情况不能够被重新赋值

 

1、通过构造函数为final变量赋值

2、通过方法未final变量赋值

3、final修饰的对象不是基本对象

相关文章
|
3月前
|
存储 XML JSON
用示例说明序列化和反序列化
用示例说明序列化和反序列化
|
3月前
|
JSON 缓存 NoSQL
redis序列化数据时,如何包含clsss类型信息?
通过配置 `com.fasterxml.jackson.databind.ObjectMapper` 的 `enableDefaultTyping` 方法,可以使序列化后的 JSON 包含类信息。
52 2
|
3月前
|
JSON 安全 编译器
扩展类实例的序列化和反序列化
扩展类实例的序列化和反序列化
35 0
|
6月前
|
JSON Java 数据格式
实现自定义序列化和反序列化控制的5种方式
实现自定义序列化和反序列化控制的5种方式
|
7月前
使用序列化和反序列化函数archivedDataWithRootObject和unarchivedObjectOfClasses的使用和遇到问题及解决方案
使用序列化和反序列化函数archivedDataWithRootObject和unarchivedObjectOfClasses的使用和遇到问题及解决方案
155 0
|
7月前
|
存储 C#
C#中的序列化和反序列化案例
C#中的序列化和反序列化案例
|
设计模式 JSON fastjson
替换seata BusinessActionContext序列化方式,从根源上解决反序列化类型不一致问题
替换seata BusinessActionContext序列化方式,从根源上解决反序列化类型不一致问题
193 0
|
Java
匿名内部类方式构建对象导致序列化失败
###问题描述: 以下代码为问题代码: ``` public class ItemDO implements Serializable { private static final long serialVersionUID=-463144769925355007L; ... private Map langAndTitleMap; ...
2376 0
|
XML 测试技术 数据格式