什么是serialVersionUID

Java(TM)对象序列化规范中描述到:serialVersionUID用作Serializable类中的版本控件。如果您没有显式声明serialVersionUID,JVM将根据您的Serializable类的各个方面自动为您执行此操作。(http://docs.oracle.com/javase/1.5.0/docs/api/java/io/Serializable.html)

对象序列化

声明对象:

package com.shamee.demo;


public class Student {

    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

创建序列化测试类:

package com.shamee.demo;

import org.junit.jupiter.api.Test;

import java.io.*;


public class ObjectIOStreamTest {

    @Test
    public void writeToStream(){
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("student.obj")))) {
            oos.writeObject(new Student("张三", 18));
            oos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    @Test
    public void readForStream(){
        try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("student.obj")))) {
            Object o = ois.readObject();
            System.out.println(o.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

运行结果:




无聊小知识01.serialVersionUID的作用_反序列化


序列化条件

  1. 实现Serializable接口
  2. 声明serialVersionUID常量。
package com.shamee.demo;

import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 42L;
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

在此运行结果:


无聊小知识01.serialVersionUID的作用_java_02


已能够正常序列化。

Serializable接口

查看Serializable接口源码可以看出,该接口没有声明任何方法,是一个标识接口。实现该接口的对象均会被识别为可序列化对象。

/**
The serialization runtime associates with each serializable class a version
 * number, called a serialVersionUID, which is used during deserialization to
 * verify that the sender and receiver of a serialized object have loaded
 * classes for that object that are compatible with respect to serialization.
 * If the receiver has loaded a class for the object that has a different
 * serialVersionUID than that of the corresponding sender's class, then
 * deserialization will result in an {@link InvalidClassException}.  A
 * serializable class can declare its own serialVersionUID explicitly by
 * declaring a field named <code>"serialVersionUID"</code> that must be static,
 * final, and of type <code>long</code>:
 *
 * <PRE>
 * ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L; 
If a serializable class does not explicitly declare a serialVersionUID, then
* the serialization runtime will calculate a default serialVersionUID value
* for that class based on various aspects of the class, as described in the
* Java(TM) Object Serialization Specification.   However, it is strongly
* recommended that all serializable classes explicitly declare
* serialVersionUID values, since the default serialVersionUID computation is
* highly sensitive to class details that may vary depending on compiler
* implementations, and can thus result in unexpected
* InvalidClassExceptions during deserialization.
*/

从注释中可以看出,实现该接口。需要显示声明一个serialVersionUID。用于序列化运行时与每个可序列化类关联一个版本编号,称为serialVersionUID,在反序列化过程中使用验证序列化对象的发送方和接收方是否已经加载。

如果可序列化类没有显式声明serialVersionUID,则序列化运行时将计算一个默认serialVersionUID,因为默认的serialVersionUID计算为高度敏感的类细节,可能变化取决于编译器实现,从而导致意外在反序列化期间InvalidClassException。

简单的说,就是该serialVersionUID用于标识对象序列和反序列化过程中是唯一匹配的。

自动生成的serialVersionUID问题

自动生成的serialVersionUID可能会导致序列化和反序列化中导致异常。具体实验步骤:

  1. 声明student类,实现Serializable接口,不显式声明serialVersionUID
  2. 将对象进行序列化
  3. 修改student类
  4. 将student对象进行反序列化

声明student类:

package com.shamee.demo;

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;

    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

序列化:

@Test
    public void writeToStream(){
        try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("student.obj")))) {
            oos.writeObject(new Student("张三", 18));
            oos.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

序列化成功:


无聊小知识01.serialVersionUID的作用_java_03


修改student类:

package com.shamee.demo;

import java.io.Serializable;

public class Student implements Serializable {
    private String name;
    private int age;
    private int code;

    public Student() {
    }

    public Student(String name, int age, int code) {
        this.name = name;
        this.age = age;
        this.code = code;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", code=" + code +
                '}';
    }
}

接着反序列化:


无聊小知识01.serialVersionUID的作用_序列化_04


直接报错,从报错信息可以看出:serialVersionUID自动生成取决于class类的字节码。因为前后类的变更导致对象的serialVersionUID发生变化,导致对象在序列化和反序列化中找不到唯一匹配标识,从而导致异常。

所以需要显式声明serialVersionUID。

如何生成serialVersionUID

IDEA点击File->Editor->Inspections。搜索框搜索UID,选择Serializable class without 'serialVersionUID'右侧复选框打勾,右下角Severity选择warning,点击OK。


无聊小知识01.serialVersionUID的作用_反序列化_05


无聊小知识01.serialVersionUID的作用_序列化_06


之后序列化类右侧会有警告标签,点击警告标签有提示Add 'serialVersionUID' field,点击即可快速生成serialVersionUID。

无聊的小知识+1!!!