Java进阶—序列化

一.序列化和反序列化的概念

Java 对象序列化是 JDK 1.1 中引入的一组开创性特性之一,用于作为一种将 Java 对象的状态转换为字节数组,以便存储或传输的机制,以后,仍可以将字节数组转换回 Java 对象原有的状态。

基本定义

  • 序列化把对象转换为字节序列的过程称为对象的序列化
  • 把字节序列恢复为对象的过程称为对象的反序列化

对象的序列化的用途

  1. 对象持久化
    通常状况下,当程序结束时,程序中的对象不再存在。 如果通过序列化功能,将对象保存到文件中,则可以在下次程序运行是再恢复该对象。
  2. 对象传输
    通过序列化,将对象转化字节流后,两个进程可以通过网络进行远程传输数据。

二.JDK类库中的序列化

什么样的对象可以被序列化

只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以采用默认的序列化方式。

不是所有的类都有序列化的必要,比如Socket、Thread类等,这些类中并没有必要保存的信息。这也是序列化没有成为Java内部功能的原因之一。

枚举类型都会默认继承类java.lang.Enum,而该类实现了Serializable接口,所以枚举类型对象都是默认可以被序列化的。

默认序列化机制

如果仅仅只是让某个类实现Serializable接口,而没有其它任何处理的话,则就是使用默认序列化机制。使用默认机制,在序列化对象时,不仅会序列化当前对象本身,也会对该对象引用的其它对象进行序列化。同样地,这些其它对象引用的另外对象也将被序列化,以此类推。所以,如果一个对象包含的成员变量是容器类对象,而这些容器所含有的元素也是容器类对象,那么这个序列化的过程就会较复杂,开销也较大。

transient关键字

当某个字段被声明为transient后,默认序列化机制就会忽略该字段。

transient应用场景

比如一个用户有一些敏感信息(如密码,银行卡号等),为了安全起见,不希望在网络操作(主要涉及到序列化操作,本地序列化缓存也适用)中被传输,这些信息对应的变量就可以加上transient关键字。换句话说,这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。

怎么序列化

在Java API中,对象序列化接口主要由两个类提供:ObjectOutputStream,ObjectInputStream。

  • java.io.ObjectOutputStream
    代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。

  • java.io.ObjectInputStream
    代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。

为了满足保存到文件、内存、通过网络传输等不同需求,对象序列化后保存在流对象中。提供不同的流对象时,序列化后保存在相应流对象中。比如提供 FileOutputStream和FileInputStream,就保存在文件中;提供ByteArrayOutputStream、 ByteArrayInputStream,就保存在内存中。

示例代码

POJO:

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
44
45
46
47
48
49
50
51
package com.lzumetal.serialize.entity;

import java.io.Serializable;

/**
* @author liaosi
* @date 2018-08-18
*/
public class Student implements Serializable {

private static final long serialVersionUID = -4963266899668807475L;

private long id;

private String name;

private Integer age;

public Student() {
}

public Student(long id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getName() {
return name;
}

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

public Integer getAge() {
return age;
}

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

序列化工具类:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
package com.lzumetal.serialize.jdk;

import java.io.*;


public class JdkSerializeUtil {


/**
* 将对象序列化为byte数组
*
* @param object
* @return
*/
public static byte[] serializeToBytes(Object object) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(object);
oos.flush();
return baos.toByteArray();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}


/**
* 将对象序列化到文件
*
* @param object
* @param file
*/
public static void serializeToFile(Object object, File file) {
try (OutputStream fos = new FileOutputStream(file);
ObjectOutputStream oos = new ObjectOutputStream(fos)) {
oos.writeObject(object);
oos.flush();
} catch (Exception e) {
e.printStackTrace();
}
}


/**
* 从byte数字反序列化为对象
*
* @param bytes
* @return
*/
public static Object deserializeFromBytes(byte[] bytes) {
try (ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
ObjectInputStream ois = new ObjectInputStream(inputStream)) {
return ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}


/**
* 从文件反序列化成对象
*
* @param file
* @return
*/
public static Object deserializeFromFile(File file) {
try (InputStream fis = new FileInputStream(file);
ObjectInputStream ois = new ObjectInputStream(fis)) {
return ois.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
}

测试类:

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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.lzumetal.serialize.jdk;

import com.google.gson.Gson;
import com.lzumetal.serialize.entity.Student;
import org.junit.Test;

import java.io.*;

public class JdkSerializeTest {


/**
* 序列化(为byte数组)
*/
@Test
public void serializeToByteArray() {
Student student = new Student(1301, "xiaoming", 12);
byte[] bytes = JdkSerializeUtil.serializeToBytes(student);
System.out.println("序列化完成,bytes的长度是:" + bytes.length + " 字节");
for (byte aByte : bytes) {
System.out.print(aByte + " ");
}
}


/**
* 序列化(到文件)
*/
@Test
public void serializeToFile() {
Student student = new Student(1301, "xiaoming", 12);
File file = new File("d:/jdk_serialize_test");
JdkSerializeUtil.serializeToFile(student, file);
System.out.println("序列化完成,文件的大小是:" + file.length() + " 字节");

}


/**
* 反序列化(读取文件字节内容转对象)
*/
@Test
public void deserializeFromFile() {
File file = new File("d:/jdk_serialize_test");
Object object = JdkSerializeUtil.deserializeFromFile(file);
Student student = (Student) object;
System.out.println(new Gson().toJson(student));
}


/**
* 反序列化:字节数组转对象
*/
@Test
public void deserializeFromByteArray() {
Student student = new Student(1301, "xiaoming", 12);
byte[] bytes = JdkSerializeUtil.serializeToBytes(student);
Student fromBytes = (Student) JdkSerializeUtil.deserializeFromBytes(bytes);
System.out.println(new Gson().toJson(fromBytes));
}



}

------ 本文完 ------