Java Serializable系列化与反系列化(一)

2014-11-24 03:19:37 · 作者: · 浏览: 4

【引言】

将Java 对象序列化为二进制文件的Java 序列化技术是Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现Serializable 接口,使用ObjectInputStream 和ObjectOutputStream 进行对象的读写。然而在有些情况下,光知道这些还远远不够,文章列举了笔者遇到的一些真实情境,它们与Java 序列化相关,通过分析情境出现的原因,使读者轻松牢记Java 序列化中的一些高级认识。

【系列化serialVersionUID问题】 www.2cto.com

在Java系列化与反系列化中,虚拟机是否允许反序列化,不仅取决于类路径和功能代码是否一致,一个非常重要的一点是两个类的序列化ID 是否一致(就是private static final long serialVersionUID = 1L),如果serialVersionUID不同,你将得到一个java.io.InvalidClassException,看如下代码:

view plain

package wen.hui.test.serializable;

import java.io.Serializable;

/**

* serializable测试

*

* @author whwang

* 2011-12-1 下午09:50:07

*/

public class A implements Serializable {

private static final long serialVersionUID = 2L;

public A() {

}

public void print() {

System.err.println("test serializable");

}

public static void main(String[] args) throws Exception {

}

}

view plain

package wen.hui.test.serializable;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

/**

*

* @author whwang

* 2011-12-1 下午09:54:36

*/

public class Test1 {

public static void main(String[] args) throws Exception {

// write object

String fileName = "obj";

toWrite(fileName);

// read object

toRead(fileName);

}

public static void toWrite(String fileName) throws Exception {

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(

fileName));

oos.writeObject(new A());

oos.close();

}

public static void toRead(String fileName) throws Exception {

ObjectInputStream ois = new ObjectInputStream(

new FileInputStream("obj"));

A t = (A) ois.readObject();

t.print();

ois.close();

}

}

1、直接运行Test1的main方法,运行正确;

2、先将Test1的main方法中的toRead(fileName)注释,把类A中的serialVersionUID 值改为1,运行Test1;然后在代开toRead(fileName),将toWrite(fileName)注释,同时将类A中的serialVersionUID 值改为2;运行Test1,发现抛出异常,表明如果serialVersionUID不同,即使两个“完全”相同的类也无法反序列化。

view plain

Exception in thread "main" java.io.InvalidClassException: wen.hui.test.serializable.A; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

序列化ID 在Eclipse 下提供了两种生成策略,一个是固定的1L,一个是随机生成一个不重复的long 类型数据(实际上是使用JDK 工具生成),在这里有一个建议,如果没有特殊需求,就是用默认的1L 就可以,这样可以确保代码一致时反序列化成功。那么随机生成的序列化ID 有什么作用呢,有些时候,通过改变序列化ID 可以用来限制某些用户的使用。如Facade模式中,Client 端通过Fa ade Object 才可以与业务逻辑对象进行交互。而客户端的Fa ade Object 不能直接由Client 生成,而是需要Server 端生成,然后序列化后通过网络将二进制对象数据传给Client,Client 负责反序列化得到Fa ade 对象。该模式可以使得Client 端程序的使用需要服务器端的许可,同时Client 端和服务器端的Fa ade Object 类需要保持一致。当服务器端想要进行版本更新时,只要将服务器端的Fa ade Object 类的序列化ID 再次生成,当Client 端反序列化Fa ade Object 就会失败,也就是强制Client 端从服务器端获取最新程序。

【静态变量序列】

直接看代码:

view plain

package wen.hui.test.serializable;

import java.io.Serializable;

/**

* serializable测试

*

* @author whwang

* 2011-12-1 下午09:50:07

*/

public class A implements Serializable {

private static final long serialVersionUID = 1L;

public static int staticVar = 10;

public A() {

}

public void print() {

System.err.print