0%

Java序列化与反序列化

前言

本文介绍Java的序列化与反序列化,以及它们的应用。

什么是序列化与反序列化

序列化:将堆内存中的Java对象数据通过序列化机制被表示为字节序列。

反序列化:将字节序列恢复成Java对象。

为什么需要序列化

将对象序列化为字节序列便于存储到磁盘文件中或者进行网络传输。例如,在分布式系统中,JavaBean对象需要共享数据,此时为了便于对象在网络上传输,我们需要将对象数据转化为字节序列;对象只有在JVM运行时,对象才存在于内存中,但是在实际应用中,我们需要将业务数据以对象的方式进行保存。

transient关键字与序列化

对象进行序列化时,保存的是对象的状态,即对象的成员变量。若变量由static或transient进行修饰,则不会对该变量进行序列化。哪些变量需要transient关键字进行修饰?可以根据实例中其他字段计算出该字段,例如,可以通过当前时间和出生年月计算某个人的年龄,而不用序列化age这个字段,根据需求进行具体分析。

serialVersionUID

让我们来考虑一个问题,在序列化操作完成后,我们需要对序列化后对象增加成员变量,此时若进行反序列化会报错。

为了解决这个问题,在序列化时需要增加serialVersionUID字段,指定序列化版本号,当版本号相同,即使修改了对象的成员变量,对象也可以准确的反序列化。

实例

首先,定义一个User类

1
2
3
4
5
public class User implements Serializable {//序列化实现的一种方式:实现Serializable接口
private String name;
private int age;
private transient int pwd; //被transient修饰的变量,不会对该变量进行序列化,反序列化时是默认值0
}

接下来,将序列化User的实例对象,再进行反序列化构建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class SerializableTest {
public static void main(String[] args) throws Exception {
User user = new User();
user.setName("twentytwoguns");
user.setAge(23);
user.setPwd(123456);
System.out.println(user);

ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user")); //序列化操作
oos.writeObject(user);
oos.close();

File file = new File("user");
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file)); //反序列化操作
User user1 = (User)ois.readObject();
System.out.println(user1.toString());
}
}

输出结果:

1
2
User{name='twentytwoguns', age=23, pwd=123456}
User{name='twentytwoguns', age=23, pwd=0}

若此时,需要对User类增加性别属性sex

1
2
3
4
5
6
public class User implements Serializable {
private String name;
private int age;
private transient int pwd;
private int sex;
}

在进行上述的反序列化操作时,会有异常:

因为在保存User对象后修改了User的结构,会导致serialVersionUID的值发生变化,在反序列化的时候就会出错。解决的方法是在User类中增加一个serialVersionUID变量固定序列化的版本号。

1
private static final long serialVersionUID = 7981560250804078637l;