关于java序列化问题

1.场景描述:最近在看序列化,遇到一些疑惑

  //父类
  public class parent implements Serializable{
        private static final long serialVersionUID = 7604030015683112269L; //UID
        private String name;
        
        //setter/getter...
  }
  //子类
  public class Child extends parent {
        //private String attr1;//序列化时注释此行代码,反序列化时取消注解
  }

  //测试类
  public class Test{
    public static void main(String[] args) throws Exception{
        //serializeTest(); //先序列化,添加子类新属性后,注释并反序列化
        Child c = deserializeTest();
        System.out.println(c.toString());
    }

    /*序列化*/
    public static void serializeTest() throws IOException {
        Child c= new Child();
        c.setName("wyy");
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream(new File("d:/test.txt")));
            oos.writeObject(c);
            System.out.println(c.toString());
            System.out.println("序列化success");
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            oos.close();
        }

    }

    /*反序列化*/
    public static Rich deserializeTest() throws IOException {
        Child c = null;
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(new File("d:/test.txt")));
            c = (Child) ois.readObject();
            System.out.println("反序列化成功");
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }finally {
            ois.close();
        }

        return c;
    }
  }

2.问题描述:

a.父类实现序列化接口
b.子类继承父类
c.先序列化子类对象,子类对象添加新属性后,再反序列化。抛出 InvalidClassException异常,UID不一致..
d.子类添加序列化ID后,反序列化正常执行  
(个人理解是序列号类似“暗号”,序列化后,即使类新增了属性“改头换面”,只要“暗号”对上,
就可以正常识别旧的对象数据,反序列化就可以正常执行)

3.疑惑点:

    在日常开发中,没有太在意序列化ID。父类实现序列化接口,当子类新增属性后,也没有遇到过上述异常啊。
猜想是因为对象新增属性,对应的数据库也会新增相应字段,所以反序列化时仍能对应上? 
但是平时也有用@Transient注解一些暂存字段,数据库没有对应列,也没遇到过InvalidClassException异常(猜想错误...)。

为啥测试用例中,如果没有serialVersionUID,子类添加新属性后再反序列化,会报错,而开发中常增加类的属性,且类中没有serialVersionUID,却没有报错?是因为连接了数据库吗??

==========================================================================
望对此了解的大佬,指点迷津...谢谢

阅读 2.4k
2 个回答

不知道楼主有没有试过memcache等缓存来做测试。你的测试方法用的是java的对象序列化,当然对class字节码有严格的判断,在实际开发中,如果场景的是数据库中取出这个实体类,或者保存到数据库,这个过程并不涉及到java的对象序列化,所以可以任意添加字段。但是如果涉及到缓存存储,比如某些memcache的库存储对象就是用java的对象序列化来保存,这个时候就会有序列化、反序列化版本不一致的问题了。所以这个问题还是要看使用场景有没有设计到java的对象序列化。

测试如下
图片描述

序列化一个对象 B继承A A实现序列化接口 但不增加版本id 成功写入文件

接下来读取
图片描述

同样成功读取。
此时 给B 增加一个属性

clipboard.png

报错 版本id 不一致

此时我们 调用 w 方法 覆盖写入

clipboard.png

再调用r 方法成功读取
clipboard.png

测试代码

package com.iking.t;

import java.io.*;

class A implements Serializable {
    //private static final long serialVersionUID = 383079475457546876L;
}

class B extends A {
    private String ex;
}

public class Run {
    public static void main(String[] args) throws Exception {
        r();
    }
    private static void w() throws Exception {
        File f = new File("d:\\serializable");
        if (!f.exists()) f.createNewFile();
        OutputStream out = new FileOutputStream(f);
        ObjectOutputStream ojn = new ObjectOutputStream(out);
        ojn.writeObject(new B());
        ojn.close();
        System.out.println("成功写入");
    }

    private static void r() throws Exception {
        File f = new File("d:\\serializable");
        InputStream in = new FileInputStream(f);
        ObjectInputStream ojn = new ObjectInputStream(in);
        Object o = ojn.readObject();
        ojn.close();
        B b = (B) o;
        System.out.println("成功读取");
    }
}

在项目中 entity 类总会去继承一些类 在这些类中都处理过 版本id的问题 所以子类不需要进行任何处理

结论

serialVersionUID  即使版本id 根据一个类的成员根据一定算法计算得出  若未指定 serialVersionUID  则在序列化时候 默认生成一个serialVersionUID 当你在序列化文件生成之后  给类增删成员 则改类的 serialVersionUID 值会发生变化  反序列化的时候  会比较 serialVersionUID   若序列化文件与类的serialVersionUID  不一致 则抛出此异常

clipboard.png

源码中也是直接这么比较的 不一致则抛出InvalidClassException 异常

推荐问题