Java中的SerialVersionUID

序列化及SergalVersionUID困扰着许多Java开发人员。我经常会看到这样的问题,什么是SerialVersionUID,如果实现了Serializable接口的类中没有定义SerialVersionUID的话会怎样?抛开它的复杂性以及不太常用不说,一个原因就是Eclipse在缺少了SerialVersionUID之后的给出的警告提示:"The Serializable class Customer does not declare a static final SerialVersionUID field of type long”。从本文中你可以了解到SerialVersionUID的基础知识以及它在序列化及反序列化中所起到的作用。当你通过实现java.io.Serializable接口将一个类声明为可序列化的,并且你没有通过 Externalizable接口自定义自己的序列化方式的话,Java会在运行时使用默认的序列化机制将这个类持久化到磁盘里面。在序列化的过程中,Java会在运行时为这个类生成一个版本号,这样它后面才可以进行反序列化。这个版本号就是SerialVersionUID。如果在反序列化的过程中,SerialVersionUID不匹配的话,这个反序列化的过程就会失败,同时会抛出java.io.InvalidClassException异常,并打印出类的名字以及对应的SerialVersionUID。一个快速的解决办法就是在你的类中声明一个private static final long类型的SerialVersionUID常量。本文中你将了解到为什么要使用SerialVersionUID,以及如何使用JDK工具serialver来生成这个ID。如果你从未了解过序列化的话,你可以看下这篇Java序列化的十个面试题来检验下你的相关知识,看看往下读的话理解起来有没有难度。和并发以及多线程类似,序列化是另一个值得反复阅读的主题。

Java中为什么使用SerialVersionUID

我刚才也说过了,如果你的类里没有声明一个static, final并且是long类型的SerialVersionUID属性的话,Java的序列化机制会替你生成一个的。它的生成机制受很多因素的影响,包括类中的字段,还有访问限制符,类实现的接口,甚至是不同的编译器实现,任何类的修改或者使用了不同的编译器生成的SerialVersionUID都各不相同,很可能最终导致重新加载序列化的数据中止。依赖Java的序列化机制来生成这个ID的话太危险了,这就是为什么推荐你在需要序列化的类中自己声明一个SerialVersionUID的原因。我强烈推荐你读一下Joshua Bloch的Java经典著作,Effective Java 来了解下Java的序列化机制以及错误的处理所导致的问题。顺便提一句,JDK提供了一个叫serailver的工具,它在JAVAHOME文件夹下的bin目录 里,在我的机器上是:C:\Program Files\Java\jdk1.6.026\bin\serialver.exe,你可以用它来为老的class文件来生成SerialVersionUID。如果你修改了你的类,破坏了序列化的过程从而导致你的应用程序无法重新加载序列化的数据的时候,这个工具就非常有用了。你可以用这个工具来为老的实例重新生成SerialVersionUID,然后通过在你的类中声明一个static final long类型的SerialVersionUID来显式地指定它。同时,不管是出于性能还是安全的原因,都强烈推荐使用自定义的二进制格式进行序列化,Effective Java里也曾多次提到了这点,里面对自定义格式的好处有详细的介绍。

如何使用JDK工具serialver来生成SerialVersionUID

你可以使用JDK的serialver来生成类的SerialVersionUID。这个对于现有的类来说尤其有用,它返回的SerialVersionUID很适用复制使用。你可以像下例中这样使用serialver:

$ serialver use: serialver [-classpath classpath] [-show] [classname…]

$ serialver -classpath . Hello Class Hello is not Serializable.

$ serialver -classpath . Hello Hello: static final long SerialVersionUID = -4862926644813433707L;

你还可以通过运行$serialver -show来以GUI的形式来使用serailver工具,这会打开一个序列化版本号的查看器,它接收完整的类名并输出对应的序列化版本号。

总结

现在我们已经知道什么是SerialVersionUID以及为什么在类中声明SerialVersionUID是如此重要了,可以回顾下一些相关的重要的概念了。

SerialVersionUID是用于序列化数据的。只有当序列化的实例的SerialVersionUID和当前类的匹配才能进行反序列化。 如果你不在类中声明SerialVersionUID的话,Java会在运行时替你生成一个,不过这个生成的过程会受到类元数据包括字段数,字段类型,字段的访问限制符,类实现的接口等因素的影响。在Oralce的官方文档中你可以找到关于序列化的准确的描述信息。 推荐自己声明privae static final long类型的SerialVersionUID字段来避免默认机制。如果你没这么做的话,像Eclipse的一些IDE会提示警告信息:"The Serializable class Customer does not declare a static final SerialVersionUID field of type long"。尽管你可以通过Window > Preferences > Java > Compiler > Errors / Warnings > Potential Programming Problems 将这个功能屏蔽掉,但我建议你还是不要这么做。我见过的唯一例外的情况就是不需要恢复数据的情况下。下面是在Eclipse IDE中这个错误的截图,你需要做的就是点击一下快速修复。

你可以使用JDK提供的serailver工具来给Java类生成序列版本号。它也有一个GUI界面,可以通过传递-show参数启用它。 Java序列化的最佳实践就是显式地声明SerialVersionUID,避免反序列化过程中可能出现的问题,尤其是当你运行的C/S模式的应用依赖于RMI进行数据序列化的时候。

这就是Java中SerialVersionUID 的全部内容。现在我们知道在类中声明SerialVersionUID 的重要性了。感谢你的IDE给了你这个提示,不然你的类反序列化的时候可能就会出现问题了。

如果你想了解下关于序列化相关的更多的一些知识,可以参考下下面几篇不错的文章:

Java中transient和volatile变量的区别 Java中Serializable和Externalizable接口的不同 Java中何时应该使用transient变量

Java中的SerialVersionUID的更多相关文章

  1. Java中serialVersionUID

    报错信息如下: Adds a default serial version ID to the selected type. Use this option to add a user-defined ...

  2. Q:java中serialVersionUID的作用

    @转载自:http://www.cnblogs.com/guanghuiqq/archive/2012/07/18/2597036.html   简单来说,Java的序列化机制是通过在运行时判断类的s ...

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

     1.什么是序列化和反序列化 序列化就是将java对象转成字节序列的过程:反序列化就是将字节序列转成java对象的过程. java中,序列化的目的一种是需要将对象保存到硬盘上,一种是对象需要在网络中传 ...

  4. 5. Java中序列化的serialVersionUID作用

    Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象. 序列化是Java中实现持久化存储的一种方法:为数据传输提供了线路级对象表示法. Java的序列化机制是通过在运行时判断 ...

  5. Java中的关键字 transient

    先解释下Java中的对象序列化 在讨论transient之前,有必要先搞清楚Java中序列化的含义: Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息 ...

  6. java中可定制的序列化过程 writeObject与readObject

    来源于:[http://bluepopopo.iteye.com/blog/486548] 什么是writeObject 和readObject?可定制的序列化过程 这篇文章很直接,简单易懂.尝试着翻 ...

  7. 深入理解Java中的String

    一.String类 想要了解一个类,最好的办法就是看这个类的实现源代码,来看一下String类的源码: public final class String implements java.io.Ser ...

  8. 第一弹:Java 中创建对象的4种方式

    Java 是面向对象的语言,不可避免的,"对象"这个概念是 Java 语言的核心部分,这里来简单讨论一下在 Java 中创建一般对象的方法. 总结下来有以下4种创建对象的方法: 使 ...

  9. Java中的序列化Serialable高级详解

    来自[http://blog.csdn.net/jiangwei0910410003/article/details/18989711] 引言 将 Java 对象序列化为二进制文件的 Java 序列化 ...

随机推荐

  1. SQL server Error Number

    描述 HY000 所有绑定列都是只读的. 必须是可升级的列,以使用 SQLSetPos 或 SQLBulkOperations 更改或插入行. HY000 已检测到一个旧 netlib (%s).请删 ...

  2. scrapy使用PhantomJS爬取数据

    环境:python2.7+scrapy+selenium+PhantomJS 内容:测试scrapy+PhantomJS 爬去内容:涉及到js加载更多的页面 原理:配置文件打开中间件+修改proces ...

  3. 《algorithms Unlocked》读书笔记3——计数排序

    <Algorithms Unlocked>是 <算法导论>的合著者之一 Thomas H. Cormen 写的一本算法基础,算是啃CLRS前的开胃菜和辅助教材.如果CLRS的厚 ...

  4. linux下分割文件

    split -l 115 XSMD.txt -d -a 2 XSMD.txt._   注:将一个文件XSMD.txt分割成两个文件,每个大小115

  5. 设计一个有getMin功能的栈(1)

    题目: 实现一个特殊的栈,在实现栈的基本功能的基础上,再实现返回栈中最小元素的操作. 要求: 1.pop.push.getMin操作的时间复杂度都是O(1) 2.设计的栈类型可以输用现成的栈结构 解答 ...

  6. python之 正则表达式

    简介 Python 自1.5版本起增加了re 模块,它提供 Perl 风格的正则表达式模式.Python 1.5之前版本则是通过 regex 模块提供 Emacs 风格的模式.Emacs 风格模式可读 ...

  7. linux中使用Python IDE pycharm教程

    今天使用vim编辑Python 并在linux中终端调试的时候,发现每次不是自己想要输出结果的时候,就要用vim编辑代码,再重新回到终端,比较浪费时间.搜索发现pycharm这一个Python ide ...

  8. apache2.2版本安装说明

     部署httpd服务  apache是一个模块化设计的服务,所谓的模块化就是将各个功能,特性都独立的分开,这样设计的好处是可扩展性强,各个功能之间的依赖性相对较弱,在后期修改,升级,添加新功能是非常方 ...

  9. MYSQL:python 3.x连接数据库的方式

    我们想要在我们的mython程序中使用mysql,首先需要安装pymysql模块,安装方式可以使用cmd命令安装, pip3.x install pymysql 首先在我们连接数据库之前先创建一个us ...

  10. 手把手教你构建 Kubernetes 1.8 + Flannel 网络(一)

    一.环境说明 操作系统:CentOS7 Kubernetes版本:v1.8.4 Docker版本:v17.06-ce Flannel 版本: flannel-v0.9.1 二.Ntp 服务器配置   ...