简介

  • 序列化:将对象写入到IO流中
  • 反序列化:从IO流中恢复对象
  • 意义:序列化机制允许将实现序列化的Java对象转换为字节序列,这些字节序列可以保存在磁盘上,或通过网络传输,以达到以后恢复成原来的对象。序列化机制使得对象可以脱离程序的运行而独立存在。
  • 使用场景:所有可在网络上传输的对象都必须是可序列化的,比如RMI(remote method invoke,即远程方法调用),传入的参数或返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的java对象都必须是可序列化的。通常建议:程序创建的每个JavaBean类都实现Serializeable接口。

实现方式

序列化

  1. 创建一个ObjectOutputStream输出流
  2. 调用ObjectOutputStream对象的writeObject输出可序列化对象
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
28
29
30
31
32
import java.io.*;

public class Person implements Serializable {//实现Serializable接口
private String name;
private int age;

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

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class WriteObject {
public static void main(String args[]) {
try {//创建一个ObjectOutStream流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\test.txt"));
//将对象序列化到文件
Person person = new Person("wawyw", 18);
oos.writeObject(person);
System.out.println("序列化成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}

反序列化

  1. 创建一个ObjectInputStream输入流
  2. 调用ObjectInputStream对象的readObject()得到序列化的对象
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
28
29
30
31
32
import java.io.*;

public class Person implements Serializable {//实现Serializable接口
private String name;
private int age;

public Person(String name, int age) {
this.age = age;
this.name = name;
System.out.println("反序列化调用我这个构造方法了嘛?");
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
class ReadObject {
public static void main(String args[]) {
try {//创建ObjectInputStream流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\test.txt"));
Person wawyw = (Person) ois.readObject(); //反序列化成对象
System.out.println(wawyw);
}catch (Exception e){
e.printStackTrace();
}
}
}
// Person{name='wawyw', age=18}

输出告诉我们,反序列化并不会调用构造方法。反序列的对象是由JVM自己生成的对象,不通过构造方法生成。

transient

使用transient关键字选择不需要序列化的字段。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import java.io.*;

public class Person implements Serializable {
//不序列化年龄和名字
private transient String name;
private transient int age;
private int height;
private transient boolean singlehood;
public Person(String name, int age){
this.name = name;
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", height=" + height +
", singlehood=" + singlehood +
'}';
}
}
class TransientTest{
public static void main(String[] args){
try {//创建ObjectOutputStream流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\a.txt"));
//创建对象
Person person = new Person("wawyw",18);
//序列化它
oos.writeObject(person);
//创建ObjectInputStream流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\a.txt"));
//反序列化
Person wawyw = (Person) ois.readObject();
//打印出来
System.out.println(wawyw);
}catch (Exception e){
e.printStackTrace();
}
}
}
//Person{name='null', age=0, height=0, singlehood=false}

使用transient修饰的属性,java序列化时,会忽略掉此字段,所以反序列化出的对象,被transient修饰的属性是默认值。对于引用类型,值是null;基本类型,值是0;boolean类型,值是false。

自定义序列化

java提供了可选的自定义序列化。可以进行控制序列化的方式,或者对序列化数据进行编码加密等。

通过重写writeObject与readObject方法,可以自己选择哪些属性需要序列化, 哪些属性不需要。如果writeObject使用某种规则序列化,则相应的readObject需要相反的规则反序列化,以便能正确反序列化出对象。这里展示对名字进行反转加密。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.io.*;

public class Person implements Serializable {
private String name;
private int age;
//省略构造方法,get及set方法

private void writeObject(ObjectOutputStream out) throws IOException {
//将名字反转写入二进制流
out.writeObject(new StringBuffer(this.name).reverse());
out.writeInt(age);
}

private void readObject(ObjectInputStream ins) throws IOException,ClassNotFoundException{
//将读出的字符串反转恢复回来
this.name = ((StringBuffer)ins.readObject()).reverse().toString();
this.age = ins.readInt();
}
}