概述

对象序列化:

所谓的对象序列化就是将对象保存到磁盘中,或者在网络中传输对象。

这种机制就是使用一个字节序列表示一个对象,该字节序列包含:

  • 对象的类型
  • 对象的数据
  • 对象中存储的属性等信息

字节序列写到文件之后,相当于文件中持久保存了一个对象的信息

对象反序列化:

对象反序列化就是将 对象的字节序列从文件中读取回来,重构对象,对它进行反序列化。

对象序列化准备工作

实现Serializable接口

如果要将,对象序列化,该对象必须实现Serializable接口,如下:

1
2
3
4
5
6
class People implements Serializable {
private static final long serialVersionUID = 42L;
private String name;
private int age; //不想被序列化 transient
//....
}

Serializable接口表示该类 启用 类的序列化,也就是允许该类被序列化。该接口没有实现方法,只是一个标识性的接口。

如果需要序列化或者反序列化的类不实现该接口,那么在序列化或者序列化过程中抛出异常NotSerializableException

最好显式声明一个serialVersionUID

什么是serialVersionUID

序列化运行时将每个可序列化的类称为serialVersionUID的版本号相关联。

该序列号在反序列化期间用于验证序列化对象的发送者和接收者是否已加载与该序列化兼容的对象的类。 如果接收方加载了一个具有不同于相应发件人类的serialVersionUID的对象的类,则反序列化将导致InvalidClassException

简而言之:

相当于Java的版本号,保证序列化的对象反序列化兼容的对象 的版本相同才能反序列化。

如果没有显式地声明一个serialVersionUID

则序列化运行时将根据**Java(TM)对象序列化规范中所述的类的各个方面**计算该类的默认serialVersionUID

默认的serialVersionUID计算对类详细信息非常敏感,这可能会因编译器实现而异,因此可能会在反InvalidClassException化期间导致InvalidClassExceptionInvalidClassException

比如将如下类的对象序列化

1
2
3
4
5
6
class People implements Serializable {

private String name;
private int age;
//Geter 和 Seter....省略
}

然后在反序列化之前,修改这个类,添加一个属性或者删除一个属性

1
2
3
4
5
6
7
class People implements Serializable {

private String name;
private int age;
private String address
//Geter 和 Seter....省略
}

就会抛出InvalidClassException异常。异常信息:

stream classdesc serialVersionUID = 6500785223629970781, local class serialVersionUID = -4354424114631597798

异常信息表明,流对象发”版本号“是6500785223629970781,当前接收的类的”版本号“是-4354424114631597798。

版本号不一致!这说明默认的serialVersionUID 对类的细节非常敏感!

但如果我们显式的声明serialVersionUID就不会出现这种问题。需要说明的是,**该字段必须是静态的,最终的,类型是long **

显式声明一个serialVersionUID

1
2
3
4
5
6
7
class People implements Serializable {
private static final long serialVersionUID = 42L;

private String name;
private int age;
//Geter 和 Seter....省略
}

其他需要注意的

  • 序列化的类必须有可访问的无参构造函数
  • 序列化的类不能包含未知的数据类型

否则都会引发InvalidClassException异常。

如果你不想类中某个字段不被序列化,可以使用transient关键字修饰

对象序列化

  • 对象序列化使用objectOutputStream类。
    • 方法:void writeobject (object obj):将指定的对象写入objectOutputStream
  • 反序列化使用objectInputStream类。
    • 方法:object read0bject ():从objectInputStream读取一个对象

了解一下就行了,真正开发时,用的很少。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class UID {
public static void main(String[] args) throws IOException, ClassNotFoundException {
write();
read();
}
public static void write() throws IOException {
People peo = new People("张三",23);
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("D:\\oos.txt")
);
oos.writeObject(peo);
oos.close();
}

public static void read() throws IOException, ClassNotFoundException {
ObjectInputStream ois = new ObjectInputStream(
new FileInputStream("D:\\oos.txt")
);
Object object = ois.readObject();
People people = (People) object;
System.out.println(people.getName());
ois.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class People implements Serializable {
private static final long serialVersionUID = 42L;
private String name;
private transient int age; //不想被序列化 transient

public People(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

}

__END__