Serializable接口中serialVersionUID的作用 示例
来源:互联网 发布:手机渲染视频软件 编辑:程序博客网 时间:2024/05/29 12:34
在Eclipse中,一个对象如果实现了Serializable接口,会有一个警告信息:
The serializable class Student does not declare a static final serialVersionUID field of type long
点击警告信息,会给出3个修复建议,其中第二个是:
add generated serial version id
点击此建议,就会自动生成如下代码(具体数值不定):
private static final long serialVersionUID = 6859324283664879676L;
这个值是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。
那么这个常量serialVersionUID有什么作用呢?如果忽略这个警告信息,不定义这个常量又会如何?
先来明确一下对象序列化的含义和用途:
把Java对象转换为字节序列的过程称为对象的序列化。
把字节序列恢复为Java对象的过程称为对象的反序列化。
对象的序列化主要有两种用途:
(1) 把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;
(2) 在网络上以字节序列的方式传输对象。
我们以第一种用途为例,用具体代码测试serialVersionUID的作用
代码段一:类Student version1.0,这个类将会被序列化保存到文件中
/** * Student * @version 1.0 */public class Student implements Serializable {private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "姓名:" + name + " 年龄:" + age;}}
代码段二:方法write(序列化)
/** * 将一个对象数据写入到一个二进制文件 * */private static void write(){try {File file = new File("student_object.dat");FileOutputStream fos = new FileOutputStream(file);ObjectOutputStream oos = new ObjectOutputStream(fos);Student std = new Student("Tom", 15);oos.writeObject(std);oos.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}}代码段三:方法read(反序列化)
/** * 从一个二进制文件中读取对象数据并打印对象信息 * */private static void read(){try {File file = new File("student_object.dat");FileInputStream fis = new FileInputStream(file);ObjectInputStream ois = new ObjectInputStream(fis);Object obj = ois.readObject();System.out.println(obj);ois.close();} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}代码段四:调用
public static void main(String[] args) {write();read();}运行结果:姓名:Tom 年龄:15
可以看到,在没有定义serialVersionUID这个常量的情况下,序列化和反序列化过程都没有问题。
但是,如果软件升级,Studen类进行了扩展,加入了一个变量:number(学号),如下
代码段五:类Student version2.0
/** * Student * @version 2.0 */public class Student implements Serializable {private String name;private int age;private int number;public Student(String name, int age) {this.name = name;this.age = age;}public Student(String name, int age, int number) {this.name = name;this.age = age;this.number = number;}@Overridepublic String toString() {return "姓名:" + name + " 年龄:" + age + " 学号:" + number;}}这个时候,再调用read方法读取之前保存的student_object.dat 文件,就会导致兼容性问题,因为要把旧版的Student反序列化成新版的Student,而新版中多了一个变量,就会抛出异常信息:
java.io.InvalidClassException: com.test.io.Student; local class incompatible: stream classdesc serialVersionUID = 6859324283664879676, local class serialVersionUID = -5807139062119555074
意思是从流中读取的类Student(v1.0)的serialVersionUID和本地类Student(v2.0)的serialVersionUID不一致。
好,这时候serialVersionUID终于出场了^_^
前面说过,这个值是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段。它其实起了一个在文件内部声明版本的作用,前面因为我们忽略了Eclipse的警告,没有定义这个常量serialVersionUID,所以,虚拟机会分别计算出stream中读取的类的serialVersionUID和本地对应的类的serialVersionUID,然后两者做对比,如果一致,则认为是同一版本,继续反序列化操作;如果不一致,则认为是不同版本,序列化过程可能会丢失文件信息,所以抛出java.io.InvalidClassException 异常。
那么如何避免此类问题呢?就是听从Eclipse的建议,在Student类中自己指定或者自动生成一个serialVersionUID,如下:
代码段:类Student version 1.1
/** * Student * @version 1.1 */public class Student implements Serializable {private static final long serialVersionUID = 6859324283664879676L;private String name;private int age;public Student(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "姓名:" + name + " 年龄:" + age;}}
调用上面的write方法,将定义了serialVersionUID的Student写入文件中,稍后再用。
然后软件升级,Student类扩展,加入变量:number(学号),
同时:保持serialVersionUID的值不变
如下:
/** * Student * @version 2.1 */public class Student implements Serializable {private static final long serialVersionUID = 6859324283664879676L;private String name;private int age;private int number;public Student(String name, int age) {this.name = name;this.age = age;}public Student(String name, int age, int number) {this.name = name;this.age = age;this.number = number;}@Overridepublic String toString() {return "姓名:" + name + " 年龄:" + age + " 学号:" + number;}}
然后调用之前的read方法,读取刚才生成的 Student v 1.1版的文件
运行结果:姓名:Tom 年龄:15 学号:0
测试结果表明,在定义了同一个serialVersionUID值之后,类做了扩展的情况下,依然保持了兼容性,能将老版本时生成的序列化文件反序列化为新版的类,只是其中新增的的变量为默认值。
简言之:serialVersionUID 的作用就是保持兼容性。
扩展问题一:
在read方法中对文件进行反序列化操作的时候,java虚拟机怎么知道要把文件中的数据转换成Student类呢?还打印出了Student类的相关信息?
答案就在文件中,用EditPlus打开序列化文件 student_object.dat,选择“16进制查看器”,文件内容显示如下:
可以看到,文件中包含了包名、类名等信息,所以java虚拟机会按图索骥,找到相关的本地类,然后进行反序列化
如果随便用一个文本文件,改名为student_object.dat,然后读取,会导致转换失败,抛出IOException。
扩展问题二:
上面所说的“软件升级,类扩展”还有很多方式,比如:
1. 如果用2.1版的Student 写入文件,再用1.1版的Student 来读取,会出现什么结果?
2. 如果Student 类扩展的时候没有新增字段,而是修改了已有的变量名或变量类型,又会出现什么结果?
类似的可能还有很多种,有兴趣的朋友可以自己试试。
- Serializable接口中serialVersionUID的作用 示例
- Serializable接口serialVersionUID的作用
- Serializable中serialVersionUID的作用
- Serializable序列化中serialVersionUId的作用
- Java中实现Serializable接口的类中serialVersionUID常量的作用
- Serializable 和serialVersionUID 的作用
- Java中为什么实现了Serializable接口就具备了被序列化的能力以及serialVersionUID的作用
- 序列化Serializable serialVersionUID的作用
- Serializable序列化接口serialVersionUID的理解
- JavaBean (实现 Serializable(可序列化) 接口的类) 里 serialVersionUID 的作用
- Java中实现Serializable接口的作用
- java中实现Serializable接口的作用
- Serializable接口的作用
- serializable 接口的作用
- Java中序列化接口Serializable接口的作用
- Java中实现Serializable接口为什么要声明serialVersionUID?
- Java中实现Serializable接口为什么要声明serialVersionUID?
- Java中实现Serializable接口为什么要声明serialVersionUID?
- verilog 加法器和 D触发器在一起实现的 电路图
- 基于JAVA的简易缓存系统
- 一个IIC的5V和3.3V电平转换的经典电路分享
- jQuery的.bind()、.live()和.delegate()之间的区别
- MyEclipse10.1破解工具及方法图解
- Serializable接口中serialVersionUID的作用 示例
- LeetCode Regular Expression Matching
- 苹果信息推送服务(Apple Push Notification Service)使用总结
- 工作三年的程序员如何在技术上实现自我的提升
- iOS 存储数据包括自定义类
- 在 Microsoft .NET Framework 中构建简单 Comet 应用程序
- 在SQL Server 2005 中开启SSL(图文结合)
- ural 1260. Nudnik Photographer 规律dp
- GTK+ window环境的搭建