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变量

时间: 2024-08-07 13:09:26

Java中的SerialVersionUID的相关文章

Java中serialVersionUID的解释

Java中serialVersionUID的解释 serialVersionUID作用:        序列化时为了保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性. 有两种生成方式: 一个是默认的1L,比如:private static final long serialVersionUID = 1L; 一个是根据类名.接口名.成员方法及属性等来生成一个64位的哈希字段,比如:        private static final   long     serialVersionU

71. Java中序列化的serialVersionUID作用

Java序列化是将一个对象编码成一个字节流,反序列化将字节流编码转换成一个对象. 序列化是Java中实现持久化存储的一种方法:为数据传输提供了线路级对象表示法. Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的.在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常. Eclipse中The

Java中serialVersionUID

报错信息如下: Adds a default serial version ID to the selected type. Use this option to add a user-defined ID in combination with  custom serialization code if the type did undergo structural  changes since its first release. 推荐两篇文章:http://zengxiankang2011

6-探秘Java中的String、StringBuilder以及StringBuffer

相信String这个类是Java中使用得最频繁的类之一,并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String.StringBuilder和StringBuffer这几个类,分析它们的异同点以及了解各个类适用的场景.下面是本文的目录大纲: 一.你了解String类吗? 二.深入理解String.StringBuffer.StringBuilder 三.不同场景下三个类的性能测试 四.常见的关于String.StringBuffer的面试题(辟谣网上流传的一些曲解String类

Java中的浅拷贝与深拷贝

Object中的clone方法: protected native Object clone() throws CloneNotSupportedException; 创建并返回此对象的一个副本.“副本”的准确含义可能依赖于对象的类.这样做的目的是,对于任何对象 x,表达式: x.clone() != x 为 true,表达式: x.clone().getClass() == x.getClass() 也为 true,但这些并非必须要满足的要求.一般情况下: x.clone().equals(x

Java中是构造器创建对象吗?

首先,这里说明” Java中是构造器创建对象 “这句话是完全错误的. Java中构造器的作用主要是为了初始化变量的值...其实在执行构造器之前,Java对象所需要的内存空间,已经产生了... 一般可以理解为由new关键字创建出来的哦. 在某些时候,一般通过new 关键字创建出相应的对象后,可以通过对应的构造方法来初始化对应变量的值. 但在某些特殊情况下,我们可以不通过new关键字而创建出相关的对象 常见的两种不通过new 关键字创建对象的方式如下: 1)通过Java的序列化和反序列化,来创建相关

Java中的异常和处理详解

原文出处:代码钢琴家 简介 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止,还是输出错误给用户?或者用C语言风格:用函数返回值作为执行状态?. Java提供了更加优秀的解决办法:异常处理机制. 异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰. Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手

Java中的对象序列化

好久没翻译simple java了,睡前来一发.译文链接:http://www.programcreek.com/2014/01/java-serialization/ 什么是对象序列化 在Java中,对象序列化指的是将对象用字节序列的形式表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,并且支持从数据库或文件中反序列化,从而在内存中重建对象: 为什么需要序列化 序列化经常被用于对象的网络传输或本地存储.网络基础设施和硬盘只能识别位和字节信息,而不能识别Jav

JAVA中的throws和throw的区别

Java 一直对java中的throws和throw不太理解.最近一直在查这两个方面的资料,算是能明白一点吧.如果我下面的观点哪有不对,希望指出来,我加以改进. throw:(针对对象的做法) 抛出一个异常,可以是系统定义的,也可以是自己定义的.下面举两个例子: 抛出Java中的一个系统异常: public class One { public void yichang(){ NumberFormatException e = new NumberFormatException(); throw