无聊小知识01.serialVersionUID的作用

简介: java编码过程中,经常需要对象序列化和反序列化。对象默认要实现Serializable,同时声明serialVersionUID。那么一定要声明serialVersionUID吗?为什么?

什么是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();
        }
    }
}

运行结果:

网络异常,图片无法展示
|

序列化条件

  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 +
                '}';
    }
}

在此运行结果:

网络异常,图片无法展示
|

已能够正常序列化。

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();
        }
    }

序列化成功:

网络异常,图片无法展示
|

修改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 +
                '}';
    }
}

接着反序列化:

网络异常,图片无法展示
|

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

所以需要显式声明serialVersionUID。

如何生成serialVersionUID

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

网络异常,图片无法展示
|

网络异常,图片无法展示
|

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

无聊的小知识+1!!!

相关文章
|
小程序 JavaScript
小程序bindtap 和 catchtap 的区别以及如何使用
小程序bindtap 和 catchtap 的区别以及如何使用
834 0
|
人工智能 IDE 开发工具
给IntelliJ IDEA添加AI功能
这篇文章讲解了如何在IntelliJ IDEA中安装和使用阿里云开发的通义灵码插件,以增强IDE的人工智能辅助编程功能。
11131 0
给IntelliJ IDEA添加AI功能
|
分布式计算 Go C++
初探Go语言RPC编程手法
总的来说,Go语言的RPC编程是一种强大的工具,让分布式计算变得简单如同本地计算。如果你还没有试过,不妨挑战一下这个新的编程领域,你可能会发现新的世界。
287 10
|
网络安全 数据安全/隐私保护 Windows
websocket优缺点
websocket优缺点
1175 2
|
算法 程序员
老程序员分享:nextInt和nextLine以及next方法的区别
老程序员分享:nextInt和nextLine以及next方法的区别
566 0
|
Java
Java开发中遇到12小时或者24小时制
Java开发中遇到12小时或者24小时制
306 0
|
Java 索引
java list中包含某个字符串
【2月更文挑战第9天】
1474 0
|
前端开发 UED
Netty Review - Netty自动重连机制揭秘:原理与最佳实践
Netty Review - Netty自动重连机制揭秘:原理与最佳实践
514 0
|
Java
【IDEA用法】使用IntelliJ IDEA自动生成serialVersionUID
【IDEA用法】使用IntelliJ IDEA自动生成serialVersionUID
3266 0
|
缓存 Java Nacos
13-微服务技术栈(高级):Nacos注解@RefreshScope热更新原理
在前面学习Nacos的章节中,为了实现配置的热更新我们采取了两种方式,其一就是借助于注解:@RefreshScope,那么这个注解是如何做到标识即生效的?我们尝试一起分析一下。
1507 0