一、问题

当一个对象实现 Serializable 接口时,多数 ide 会提示声明一个静态常量 serialVersionUID(版本标识),那 serialVersionUID 到底有什么作用呢?应该如何使用 serialVersionUID ?

二、探索历程

serialVersionUID 是实现 Serializable 接口而来的,而 Serializable 则是应用于Java 对象序列化/反序列化。对象的序列化主要有两种用途:

  1. 把对象序列化成字节码,保存到指定介质上(如磁盘等)

  2. 用于网络传输

现在反过来说就是,serialVersionUID 会影响到上述所提到的两种行为。那到底会造成什么影响呢?
java.io.Serializable doc 文档,给出了一个相对详细解释:
 serialVersionUID 是 Java 为每个序列化类产生的版本标识,可用来保证在反序列时,发送方发送的和接受方接收的是可兼容的对象。如果接收方接收的类的 serialVersionUID 与发送方发送的 serialVersionUID 不一致,进行反序列时会抛出 InvalidClassException。序列化的类可显式声明 serialVersionUID 的值,如下:

ANY-ACCESS-MODIFIER static final long serialVersionUID = 1L;

当不显式定义 serialVersionUID 的值时,Java 根据类的多个方面(具体可参考 Java 序列化规范)动态生成一个默认的 serialVersionUID 。尽管这样,还是建议你在每一个序列化的类中显式指定 serialVersionUID 的值,因为不同的 jdk 编译很可能会生成不同的 serialVersionUID 默认值,进而导致在反序列化时抛出 InvalidClassExceptions 异常。所以,为了保证在不同的 jdk 编译实现中,其 serialVersionUID 的值也一致,可序列化的类必须显式指定 serialVersionUID 的值。另外,serialVersionUID 的修饰符最好是 private,因为 serialVersionUID 不能被继承,所以建议使用 private 修饰 serialVersionUID。

三、例子

举例说明如下: 现在尝试通过将一个类 Person 序列化到磁盘和反序列化来说明 serialVersionUID 的作用: Person 类如下:

public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private Integer age;private String address;public Person() {}public Person(String name, Integer age, String address) {this.name = name;this.age = age;this.address = address;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", address='" + address + '\'' +'}';}
}

简单的测试一下:

@Test
public void testversion1L() throws Exception {File file = new File("person.out");// 序列化ObjectOutputStream oout = new ObjectOutputStream(new FileOutputStream(file));Person person = new Person("Haozi", 22, "上海");oout.writeObject(person);oout.close();// 反序列化ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));Object newPerson = oin.readObject();oin.close();System.out.println(newPerson);
}

测试发现没有什么问题。有一天,因发展需要, 需要在 Person 中增加了一个字段 email,如下:

public class Person implements Serializable {private static final long serialVersionUID = 1L;private String name;private Integer age;private String address;private String email;public Person() {}public Person(String name, Integer age, String address) {this.name = name;this.age = age;this.address = address;}public Person(String name, Integer age, String address,String email) {this.name = name;this.age = age;this.address = address;this.email = email;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +", address='" + address + '\'' +", email='" + email + '\'' +'}';}
}

这时我们假设和之前序列化到磁盘的 Person 类是兼容的,便不修改版本标识 serialVersionUID。再次测试如下:

@Test
public void testversion1LWithExtraEmail() throws Exception {File file = new File("person.out");ObjectInputStream oin = new ObjectInputStream(new FileInputStream(file));Object newPerson = oin.readObject();oin.close();System.out.println(newPerson);
}

将以前序列化到磁盘的旧 Person 反序列化到新 Person 类时,没有任何问题。

可当我们增加 email 字段后,不作向后兼容。即放弃原来序列化到磁盘的 Person 类,这时我们可以将版本标识提高,如下:

private static final long serialVersionUID = 2L;

再次进行反序列化,则会报错,如下:

java.io.InvalidClassException:Person local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

谈到这里,我们大概可以清楚,serialVersionUID 就是控制版本是否兼容的,若我们认为修改的 Person 是向后兼容的,则不修改 serialVersionUID;反之,则提高 serialVersionUID的值。再回到一开始的问题,为什么 ide 会提示声明 serialVersionUID 的值呢?因为若不显式定义 serialVersionUID 的值,Java 会根据类细节自动生成 serialVersionUID 的值,如果对类的源代码作了修改,再重新编译,新生成的类文件的serialVersionUID的取值有可能也会发生变化。类的serialVersionUID的默认值完全依赖于Java编译器的实现,对于同一个类,用不同的Java编译器编译,也有可能会导致不同的serialVersionUID。所以 ide 才会提示声明 serialVersionUID 的值。

Java serialVersionUID 有什么作用相关推荐

  1. Java中static的作用

    Java中static的作用 请先看下面这段程序: public class Hello{ public static void main(String[] args){ //(1) System.o ...

  2. java 代码块的作用_4种Java代码块的作用讲解

    4种Java代码块的作用讲解 时间:2017-06-28     来源:华清远见JAVA学院 今天华清Java学院小编要和大家分享的是Java代码块的作用,代码块是一种常见的代码形式,他用大括号&qu ...

  3. java 内部类 作用,内部类,java内部类的四大作用

    内部类,java内部类的四大作用 在java中,允许一个类的定义位于另一个类的内部,前者称为内部类,后者称为外部类. inner class 一般用在定义它的类或语句块之内,在外部引用它时必须给出完整 ...

  4. Java中接口的作用,为什么要写接口?

    ** Java中接口的作用 转载于:https://www.zhihu.com/question/20111251 ** **困惑:**例如我定义了一个接口,但是我在继承这个接口的类中还要写接口的实现 ...

  5. java 标识符用途_java标识符是什么?java标识符有什么作用?

    学习java的过程中,很多含义大家还是要知道的,这样才能更好的学习java,最近有小伙伴想知道java标识符是什么?那么接下来,我们就来给大家讲解一下这方面的内容. 就是程序员在定义java程序时,自 ...

  6. Java中重载的作用?

    Java中重载的作用? 重载定义:一个类中如果有多个同名方法但带有不同的参数列表,称为方法重载(Overload). 重载方法的参数列表不同是指参数的数据类型或个数或次序不同. 重载方法之间必须以参数 ...

  7. Java默认构造函数的作用

    Java默认构造函数的作用,有需要的朋友可以参考下. class Person{private String name="";private int age=0;public Pe ...

  8. Java抽象类构造器的作用

    Java抽象类构造器的作用 抽象类中的构造器方法会在子类实例化的时候调用 如下: abstract class Person {Person(){System.out.println("Pe ...

  9. java中*和**的作用 以及 /和/*和/**的区别

    一.java中*和**的作用 "*"就表示了所有的文件,但是"*"并不包括子目录下的文件: "**"匹配包含任意级子目录中所有的文件: 二. ...

最新文章

  1. 信息系统项目管理师复习第2小时
  2. ubuntu安装百度网盘
  3. winform中UI设计分辨率问题
  4. (二十七)【2021 WWW】Learning Intents behind Interactions with Knowledge Graph for Recommendation
  5. Leetcode--15. 三数之和
  6. VMware 6 个少为人知的技巧
  7. mysql update 顺序_MySQL的Update语句Set顺序问题
  8. 数据库基础:什么是SQL
  9. Spring Boot项目中使用 TrueLicense 生成和验证License(服务器许可)
  10. 欠采样临界采样matlab,信号临界采样、过采样、欠采样实验报告
  11. 不已0开头的数字正则
  12. 谷歌邮箱SMTP小白教程
  13. obs听到了自己的回音_直播连麦过程中回声回音解决方式
  14. 常用下载方式的区别-BT下载、磁力链接、电驴
  15. 机器学习 贝叶斯分类器 拉普拉斯修正
  16. __ratelimit: XXX callbacks suppressed
  17. MATLAB运动目标增强
  18. (论文加源码)基于DEAP的脑电情绪识别(CNN,RNN和两种不同的注意力机制)
  19. 打印表格用什么软件好?
  20. 服务器信息怎么备份,DNS服务器信息备份与还原教程

热门文章

  1. sublime for mac 常用快捷键
  2. Java程序员如何成为内功深厚的架构师
  3. ResponseJsonUtil
  4. iTween基础之功能简介及插件下载
  5. Python基础附件代码
  6. Oracle 12c RMAN全攻略
  7. 基于android的智慧社区物业便民服务APP(源码+系统+mysql数据库+Lw文档)
  8. css 固定宽度,文字均匀分布
  9. XXX手机APP测试计划
  10. php-cgi是什么,什么是CGI、FastCGI、PHP-CGI、PHP-FPM