Java Serializable系列化与反系列化
【引言】
将Java 对象序列化为二进制文件的Java 序列化技术是Java 系列技术中一个较为重要的技术点,在大部分情况下,开发人员只需要了解被序列化的类需要实现Serializable 接口,使用ObjectInputStream 和ObjectOutputStream 进行对象的读写。然而在有些情况下,光知道这些还远远不够,文章列举了笔者遇到的一些真实情境,它们与Java 序列化相关,通过分析情境出现的原因,使读者轻松牢记Java 序列化中的一些高级认识。
【系列化serialVersionUID问题】 www.zzzyk.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.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.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
/**
* 序列化保存的是对象的状态,静态变量属于类的状态,不会被序列化
* @author whwang
* 2011-12-1 下午10:12:06
*/
public class Test2 {
public static void main(String[] args) {
try {
// 初始时staticVar为10
ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("obj"));
out.writeObject(new A());
out.close()
补充:软件开发 , Java ,