java序列化和反序列化中的serialVersionUID有啥用

 1.什么是序列化和反序列化

  序列化就是将java对象转成字节序列的过程;反序列化就是将字节序列转成java对象的过程。

   java中,序列化的目的一种是需要将对象保存到硬盘上,一种是对象需要在网络中传输

2.序列化和反序列化的方式

  序列化和反序列化有很多种方式,如JDK类库中提供的序列化API、常用的json工具类等。本篇博客使用JDK提供的序列化API进行演示。重点说明serialVersionUID的作用。

  假设现在有一个Student类,我们要对Student类进行序列化操作

  ①该类必须实现Serializable接口

public class Student implements Serializable{

    private static final long serialVersionUID = -595470438262181967L;

    private String name ;
     
    private String sex;
    
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    
}

  ②在main方法中执行序列化和反序列化操作

    public static void main(String[] args) throws Exception {
        
        //序列化
        OutputStream os = new FileOutputStream(new File("D://student.txt"));
        ObjectOutputStream oos = new ObjectOutputStream(os);
        Student student = new Student();
        student.setName("张三");
        student.setSex("男");
        oos.writeObject(student);
        
        //反序列化
        InputStream is = new FileInputStream(new File("D://student.txt"));
        ObjectInputStream ois = new ObjectInputStream(is);
        Student student1 = new Student();
        student1 = (Student) ois.readObject();
        System.out.println(student1.getName());
    }

  输出结果:张三

  即student1对象在反序列化时进行了赋值

3.为什么要serialVersionUID

  s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D​:​ ​字​面​意​思​上​是​序​列​化​的​版​本​号​,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量。

  (1)下面进行测试,如果没有s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D会出现什么?

    ①去掉Student类中的s​e​r​i​a​l​V​e​r​s​i​o​n​U​I​D属性。执行main方法,结果显示序列化成功!输出张三

    ②修改Student类,在Student类中添加number字段

public class Student implements Serializable{

    private String name ;
     
    private String sex;

    private String number;
    
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }
    
}

  不执行序列化方法,只执行反序列化方法,结果出现异常:

Exception in thread "main" java.io.InvalidClassException: com.iot.study.serialize.Student; local class incompatible: stream classdesc serialVersionUID = -595470438262181967, local class serialVersionUID = -4254220179260112271
    at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:621)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1623)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1518)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1774)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1351)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
    at com.iot.study.serialize.Client.main(Client.java:35)

  意思是原来序列化的时候(没有指定serialVersionUID)硬盘存的class的 serialVersionUID = -595470438262181967,而当前class的serialVersionUID = -4254220179260112271。 二者不一样,无法反序列化。

  原因分析:

    serialVersionUID没有指定时,java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件 多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个number字段后,由于没有显指定 serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。

  (2)指定serialVersionUID测试

  如果为Student类显示的指定serialVersionUID,那么在序列化和反序列化的时候,即使修改了Student类中的部分内容,也能序列化成功。

4.serialVersionUID的取值

  serialVersionUID的取值是Java运行时环境根据类的内部细节自动生成的。如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。
  类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,有可能会导致不同的 serialVersionUID,也有可能相同。为了提高serialVersionUID的独立性和确定性,强烈建议在一个可序列化类中显示的定义serialVersionUID,为它赋予明确的值

  显式地定义serialVersionUID有两种用途:
    1、 在某些场合,希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有相同的serialVersionUID;
    2、 在某些场合,不希望类的不同版本对序列化兼容,因此需要确保类的不同版本具有不同的serialVersionUID。