Java中的SerialVersionUID
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的更多相关文章
- Java中serialVersionUID
报错信息如下: Adds a default serial version ID to the selected type. Use this option to add a user-defined ...
- Q:java中serialVersionUID的作用
@转载自:http://www.cnblogs.com/guanghuiqq/archive/2012/07/18/2597036.html 简单来说,Java的序列化机制是通过在运行时判断类的s ...
- java序列化和反序列化中的serialVersionUID有啥用
1.什么是序列化和反序列化 序列化就是将java对象转成字节序列的过程:反序列化就是将字节序列转成java对象的过程. java中,序列化的目的一种是需要将对象保存到硬盘上,一种是对象需要在网络中传 ...
- 5. Java中序列化的serialVersionUID作用
Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象. 序列化是Java中实现持久化存储的一种方法:为数据传输提供了线路级对象表示法. Java的序列化机制是通过在运行时判断 ...
- Java中的关键字 transient
先解释下Java中的对象序列化 在讨论transient之前,有必要先搞清楚Java中序列化的含义: Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息 ...
- java中可定制的序列化过程 writeObject与readObject
来源于:[http://bluepopopo.iteye.com/blog/486548] 什么是writeObject 和readObject?可定制的序列化过程 这篇文章很直接,简单易懂.尝试着翻 ...
- 深入理解Java中的String
一.String类 想要了解一个类,最好的办法就是看这个类的实现源代码,来看一下String类的源码: public final class String implements java.io.Ser ...
- 第一弹:Java 中创建对象的4种方式
Java 是面向对象的语言,不可避免的,"对象"这个概念是 Java 语言的核心部分,这里来简单讨论一下在 Java 中创建一般对象的方法. 总结下来有以下4种创建对象的方法: 使 ...
- Java中的序列化Serialable高级详解
来自[http://blog.csdn.net/jiangwei0910410003/article/details/18989711] 引言 将 Java 对象序列化为二进制文件的 Java 序列化 ...
随机推荐
- javascript中对象字面量与数组字面量
第一部分 我们知道JavaScript中的数据类型有基本数据类型和引用类型,其中Object类型就是非常常用的类型.那么如果创建一个Object类型的实例呢?下面我介绍两种方法: 第一:构造函数法. ...
- umask的作用[转]
umask的作用 umask 命令允许你设定文件创建时的缺省模式,对应每一类用户(文件属主.同组用户.其他用户)存在一个相应的umask值中的数字.对于文件来说,这一数字的最 大值分别是6.系统不允许 ...
- bootstrap媒体查询
Bootstrap 中的媒体查询允许您基于视口大小移动.显示并隐藏内容.下面的媒体查询在 LESS 文件中使用,用来创建 Bootstrap 网格系统中的关键的分界点阈值. /* 超小设备(手机,小于 ...
- CentOS配置SSH免密登录
假如我们有两台CentOS机器,192.168.199.101,192.168.199.102,要想在101上远程连接102可以通过ssh命令来实现 ssh 192.168.199.102 如果没有配 ...
- Nginx常用功能(5)
一.反向代理 1.多域名跳转(location) server www.test.com location / { proxy_pass http://192.168.100.100:8080/web ...
- Django模板中的数字自增
Django框架的模板提供了{% for %} 标签来进行循环 例如对集合进行循环是比较简单的 {% for row in v1 %} <div>{{row.name}}</div& ...
- Vue 组件之 Router
Vue 组件之 Router Vue 开发单页应用的时候,免不了使用Vue组件.在单页应用上如何进行组件切换? 结构如下图所示: 主页面包含Foo组件与Bar组件,在主页面中可以进行Foo与 Bar的 ...
- sql针对某一字段去重,并且保留其他字段
今天客户提了一个小需求,希望我能提供一条sql语句,帮助他对数据中 _field 这个字段的值去重,并且保留其他字段的数据.第一反应是select distinct,但这种语句在对某个字段去重时,无法 ...
- 查找第k小的元素(O(n)递归解法)
今天分享一个小技巧,虽然是小技巧但是还是很有价值的,曾经是微软的面试题.题目是这样的,一个无序的数组让你找出第k小的元素,我当时看到这道题的时候也像很多人一样都是按普通的思维,先排序在去第K个,但是当 ...
- Gym 100952I&&2015 HIAST Collegiate Programming Contest I. Mancala【模拟】
I. Mancala time limit per test:3 seconds memory limit per test:256 megabytes input:standard input ou ...