Java JDK7源码-java.io.Serializable

源码

1
package java.io;
2
3
public interface Serializable {
4
}

已整理层级关系

直接实现本接口的类

综述

实现本接口的类具备可序列化的能力。未实现本接口的类则不能进行序列化及反序列化。所有可序列化的类的子类都自动具备序列化的能力而无需在类定义中声明。本接口中没有任何方法或字段,它只是一个标志,标识实现本接口的类具备可序列化的能力。

为使非序列化的类具备序列化的能力,通常的做法为声明一个具备序列化能力的子类,子类必须承担起保存及恢复子类的public,protected,以及(如果可访问的话)封装字段的状态的责任。子类只在如下条件下才能承担起该责任:该子类的超类中有一个可访问的无参构造函数用以帮助子类初始化自身的状态,若不满足该条件则可通过编译,但在运行时会抛出异常。

在反序列化的过程中,非序列化类的字段将被该类的public或protected的无参构造函数初始化。该无参构造函数必须要能够被已实现序列化的子类访问。已实现序列化的子类的字段将从流中恢复。

在传输图结构时,可能会遇到不支持本接口的情况。此时会抛出NotSerializableException并定位到未序列化的类。

writeObject,readObject,readObjectNoData

在序列化及反序列化时需要进行特殊操作的类必须实现如下特定的方法:

1
private void writeObject(java.io.ObjectOutputStream out)
2
    throws IOException
3
private void readObject(java.io.ObjectInputStream in)
4
    throws IOException, ClassNotFoundException;
5
private void readObjectNoData()
6
    throws ObjectStreamException;

writeObject负责写特定的类的对象的状态以便于对应的readObject可以恢复它。默认情况下是通过调用out.defaultWriteObject()保存对象的状态。本方法不需要关心其所属超类或子类的状态。状态会以独立字段的形式被本方法或out.defaultWriteObject()存入ObjectOutputStream。默认写入的内容为调用对象的toString()的返回值。测试如下:

1
import java.io.FileInputStream;
2
import java.io.FileOutputStream;
3
import java.io.ObjectInputStream;
4
import java.io.ObjectOutputStream;
5
import java.io.Serializable;
6
7
public class STest {
8
9
    private static final String PATH_F = "D://data.f";
10
11
    private static void writeByFOS(Data data, String path) throws Exception {
12
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
13
        objectOutputStream.writeObject(data);
14
        objectOutputStream.close();
15
    }
16
17
    private static Data readByFIS(String path) throws Exception {
18
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
19
        Data result = (Data)objectInputStream.readObject();
20
        objectInputStream.close();
21
        return result;
22
    }
23
24
    public static void main(String args[]) throws Exception {
25
        STest.writeByFOS(new Data(0, "灵梦"), STest.PATH_F);
26
        System.out.println(STest.readByFIS(STest.PATH_F));
27
    }
28
}
29
30
class Data implements Serializable {
31
32
    private static final long serialVersionUID = 615562960517507579L;
33
34
    private int id;
35
36
    private String value;
37
38
    public Data(int id, String value) {
39
        this.id = id;
40
        this.value = value;
41
    }
42
43
    public int getId() {
44
        return id;
45
    }
46
47
    public void setId(int id) {
48
        this.id = id;
49
    }
50
51
    public String getValue() {
52
        return value;
53
    }
54
55
    public void setValue(String value) {
56
        this.value = value;
57
    }
58
59
    @Override
60
    public String toString() {
61
        return "Data [id=" + (id + 1) + ", value=" + value + value + "]";
62
    }
63
}

输出结果:

1
Data [id=1, value=灵梦灵梦]

readObject方法负责从流中读取数据并恢复类字段。本方法可以调用in.defaultReadObject()以采用默认的机制恢复对象的non-static及non-transient字段。defaultReadObject()将流中存储的对象的字段存入当前对象的对应位置。本方法处理类需要加字段的情况。本方法不需要关心其所属超类或子类的状态。状态会以独立字段的形式被writeObject方法或out.defaultWriteObject()存入ObjectOutputStream。

writeObject及readObject示例如下:

1
import java.io.FileInputStream;
2
import java.io.FileOutputStream;
3
import java.io.IOException;
4
import java.io.ObjectInputStream;
5
import java.io.ObjectOutputStream;
6
import java.io.Serializable;
7
8
public class STest {
9
10
    private static final String PATH_F = "D://data.f";
11
12
    private static void writeByFOS(Data data, String path) throws Exception {
13
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
14
        objectOutputStream.writeObject(data);
15
        objectOutputStream.close();
16
    }
17
18
    private static Data readByFIS(String path) throws Exception {
19
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
20
        Data result = (Data)objectInputStream.readObject();
21
        objectInputStream.close();
22
        return result;
23
    }
24
25
    public static void main(String args[]) throws Exception {
26
        STest.writeByFOS(new Data(0, "灵梦"), STest.PATH_F);
27
        System.out.println(STest.readByFIS(STest.PATH_F));
28
    }
29
}
30
31
class Data implements Serializable {
32
33
    private static final long serialVersionUID = 615562960517507579L;
34
35
    private int id;
36
37
    private String value;
38
39
    public Data(int id, String value) {
40
        this.id = id;
41
        this.value = value;
42
    }
43
44
    private void writeObject(ObjectOutputStream out) throws IOException {
45
        out.defaultWriteObject();
46
    }
47
48
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
49
        in.defaultReadObject();
50
    }
51
52
    public int getId() {
53
        return id;
54
    }
55
56
    public void setId(int id) {
57
        this.id = id;
58
    }
59
60
    public String getValue() {
61
        return value;
62
    }
63
64
    public void setValue(String value) {
65
        this.value = value;
66
    }
67
68
    @Override
69
    public String toString() {
70
        return "Data [id=" + id + ", value=" + value + "]";
71
    }
72
}

此时内部均仍采用默认机制。结果如下:

1
Data [id=0, value=灵梦]

若将writeObject方法体置为空,则抛出异常:

1
Exception in thread "main" java.io.EOFException
2
	at java.io.ObjectInputStream$BlockDataInputStream.readFully(ObjectInputStream.java:2744)
3
	at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1979)
4
	at java.io.ObjectInputStream.defaultReadObject(ObjectInputStream.java:500)
5
	at stest.Data.readObject(STest.java:51)
6
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
7
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
8
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
9
	at java.lang.reflect.Method.invoke(Method.java:606)
10
	at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1017)
11
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1893)
12
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)
13
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
14
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
15
	at stest.STest.readByFIS(STest.java:22)
16
	at stest.STest.main(STest.java:29)

若将readObject置为空,则抛出异常:

1
Exception in thread "main" java.io.StreamCorruptedException: invalid type code: 00
2
	at java.io.ObjectInputStream$BlockDataInputStream.readBlockHeader(ObjectInputStream.java:2508)
3
	at java.io.ObjectInputStream$BlockDataInputStream.refill(ObjectInputStream.java:2543)
4
	at java.io.ObjectInputStream$BlockDataInputStream.skipBlockData(ObjectInputStream.java:2445)
5
	at java.io.ObjectInputStream.skipCustomData(ObjectInputStream.java:1941)
6
	at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1918)
7
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1798)
8
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1350)
9
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
10
	at stest.STest.readByFIS(STest.java:22)
11
	at stest.STest.main(STest.java:29)

Serializable对象反序列化时,由于序列化与反序列化提供的class版本不同,序列化的class的super class不同于反序列化时的class的super class;或待接收的流受到了干扰;或者收到有敌意的流;或接收不完整;都会对初始化对象字段值时造成影响。如果发生以上情况时,没有定义readObjectNoData方法时,类的字段就会初始化成它们的默认值。当出现上面的情况时,readObjectNoData会取代readObject的调用。

writeReplace,readResolve

当实现序列化的类需指定一个替代类时,在写入流时需实现以下方法:

1
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

若序列化的类未实现writeReplace方法则会调用writeObject方法写入流。若序列化的类实现了writeReplace方法则会用writeReplace方法替代writeObject方法写入流,测试用例如下:

1
import java.io.FileInputStream;
2
import java.io.FileOutputStream;
3
import java.io.ObjectInputStream;
4
import java.io.ObjectOutputStream;
5
import java.io.ObjectStreamException;
6
import java.io.Serializable;
7
8
public class STest {
9
10
    private static final String PATH_F = "D://data.f";
11
12
    private static void writeByFOS(Data data, String path) throws Exception {
13
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
14
        objectOutputStream.writeObject(data);
15
        objectOutputStream.close();
16
    }
17
18
    private static Data readByFIS(String path) throws Exception {
19
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
20
        Data result = (Data)objectInputStream.readObject();
21
        objectInputStream.close();
22
        return result;
23
    }
24
25
    public static void main(String args[]) throws Exception {
26
        STest.writeByFOS(new Data(0, "灵梦"), STest.PATH_F);
27
        System.out.println(STest.readByFIS(STest.PATH_F));
28
    }
29
}
30
31
class Data implements Serializable {
32
33
    private static final long serialVersionUID = 615562960517507579L;
34
35
    private int id;
36
37
    private String value;
38
39
    public Data(int id, String value) {
40
        this.id = id;
41
        this.value = value;
42
    }
43
44
    public Object writeReplace() throws ObjectStreamException {
45
        return new Data(1, this.value + this.value);
46
    }
47
48
    public int getId() {
49
        return id;
50
    }
51
52
    public void setId(int id) {
53
        this.id = id;
54
    }
55
56
    public String getValue() {
57
        return value;
58
    }
59
60
    public void setValue(String value) {
61
        this.value = value;
62
    }
63
64
    @Override
65
    public String toString() {
66
        return "Data [id=" + id + ", value=" + value + "]";
67
    }
68
}

输出结果为:

1
Data [id=1, value=灵梦灵梦]
1
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

同理,当反序列化时,要将一个对象从流中读出来,我们如果想将读出来的对象用另一个对象实例替换,则需实现readResolve方法。测试用例如下:

1
import java.io.FileInputStream;
2
import java.io.FileOutputStream;
3
import java.io.ObjectInputStream;
4
import java.io.ObjectOutputStream;
5
import java.io.ObjectStreamException;
6
import java.io.Serializable;
7
8
public class STest {
9
10
    private static final String PATH_F = "D://data.f";
11
12
    private static void writeByFOS(Data data, String path) throws Exception {
13
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
14
        objectOutputStream.writeObject(data);
15
        objectOutputStream.close();
16
    }
17
18
    private static Data readByFIS(String path) throws Exception {
19
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
20
        Data result = (Data)objectInputStream.readObject();
21
        objectInputStream.close();
22
        return result;
23
    }
24
25
    public static void main(String args[]) throws Exception {
26
        STest.writeByFOS(new Data(0, "灵梦"), STest.PATH_F);
27
        System.out.println(STest.readByFIS(STest.PATH_F));
28
    }
29
}
30
31
class Data implements Serializable {
32
33
    private static final long serialVersionUID = 615562960517507579L;
34
35
    private int id;
36
37
    private String value;
38
39
    public Data(int id, String value) {
40
        this.id = id;
41
        this.value = value;
42
    }
43
44
    public Object readResolve() throws ObjectStreamException {
45
        return new Data(1, this.value + this.value);
46
    }
47
48
    public int getId() {
49
        return id;
50
    }
51
52
    public void setId(int id) {
53
        this.id = id;
54
    }
55
56
    public String getValue() {
57
        return value;
58
    }
59
60
    public void setValue(String value) {
61
        this.value = value;
62
    }
63
64
    @Override
65
    public String toString() {
66
        return "Data [id=" + id + ", value=" + value + "]";
67
    }
68
}

输出结果为:

1
Data [id=1, value=灵梦灵梦]

serialVersionUID

序列化运行时会将每一个实现序列化的类与一个版本号关联起来,称为serialVersionUID。该版本号被用于比对发送方及接受方所用的类是否是同一个版本。若接收方与发送方版本号不一致,则会抛出InvalidClassException。

实现序列化的类可指定serialVersionUID的值:

1
ANY-ACCESS-MODIFIER static final long serialVersionUID = xxxL;

若实现序列化的类未明确指定serialVersionUID,则序列化会在运行时依据待序列化类的各个方面计算一个默认的serialVersionUID值。然而Java强烈推荐每一个可序列化的类都明确指定自己的serialVersionUID,因为默认serialVersionUID的计算结果对类的编译结果异常敏感,因此若发送方及接收方的编译器有所差异则可能会在反序列化时抛出InvalidClassException。因此,为保证有一个无关Java编译实现的常量serialVersionUID值,序列化的类必须明确指定serialVersionUID。同时也强烈建议如果可能的话将serialVersionUID的访问权限设为private,因为serialVersionUID仅仅对其所属的类本身有用,换句话说,serialVersionUID并不是一个需继承的字段。数组类无法声明一个特定的serialVersionUID,所以它们总是使用默认的计算值,但是数组类并不需要与serialVersionUID建立起匹配关系。

测试

代码

1
import java.io.ByteArrayInputStream;
2
import java.io.ByteArrayOutputStream;
3
import java.io.FileInputStream;
4
import java.io.FileOutputStream;
5
import java.io.ObjectInputStream;
6
import java.io.ObjectOutputStream;
7
import java.io.Serializable;
8
import java.util.ArrayList;
9
import java.util.List;
10
11
public class STest {
12
13
    private static final String PATH_F = "D://data.f";
14
15
    private static void writeByFOS(List<Data> list, String path) throws Exception {
16
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(path));
17
        objectOutputStream.writeObject("使用FOS:");
18
        objectOutputStream.writeObject(list);
19
        objectOutputStream.close();
20
    }
21
22
    @SuppressWarnings("unchecked")
23
    private static List<Data> readByFIS(String path) throws Exception {
24
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(path));
25
        System.out.println((String)objectInputStream.readObject());
26
        List<Data> result = (ArrayList<Data>)objectInputStream.readObject();
27
        objectInputStream.close();
28
        return result;
29
    }
30
31
    private static ByteArrayOutputStream writeByBAOS(List<Data> list) throws Exception {
32
        ByteArrayOutputStream result = new ByteArrayOutputStream();
33
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(result);
34
        objectOutputStream.writeObject("使用BAOS:");
35
        objectOutputStream.writeObject(list);
36
        objectOutputStream.flush();
37
        return result;
38
    }
39
40
    @SuppressWarnings("unchecked")
41
    private static List<Data> readByBAIS(ByteArrayOutputStream baos) throws Exception {
42
        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
43
        System.out.println((String)objectInputStream.readObject());
44
        List<Data> result = (ArrayList<Data>)objectInputStream.readObject();
45
        objectInputStream.close();
46
        return result;
47
    }
48
49
    public static void main(String args[]) throws Exception {
50
        List<Data> list = new ArrayList<Data>();
51
        list.add(new Data(0, "灵梦"));
52
        list.add(new Data(1, "v2"));
53
        list.add(new Data(2, "v3"));
54
        list.add(new Data(3, "v4"));
55
56
        STest.writeByFOS(list, STest.PATH_F);
57
        System.out.println(STest.readByFIS(STest.PATH_F));
58
59
        ByteArrayOutputStream baos = STest.writeByBAOS(list);
60
        System.out.println(STest.readByBAIS(baos));
61
    }
62
}
63
64
class Data implements Serializable {
65
66
    private static final long serialVersionUID = 615562960517507579L;
67
68
    private int id;
69
70
    private String value;
71
72
    public Data(int id, String value) {
73
        this.id = id;
74
        this.value = value;
75
    }
76
77
    public int getId() {
78
        return id;
79
    }
80
81
    public void setId(int id) {
82
        this.id = id;
83
    }
84
85
    public String getValue() {
86
        return value;
87
    }
88
89
    public void setValue(String value) {
90
        this.value = value;
91
    }
92
93
    @Override
94
    public String toString() {
95
        return "Data [id=" + id + ", value=" + value + "]";
96
    }
97
}

测试结果

1
使用FOS:
2
[Data [id=0, value=灵梦], Data [id=1, value=v2], Data [id=2, value=v3], Data [id=3, value=v4]]
3
使用BAOS:
4
[Data [id=0, value=灵梦], Data [id=1, value=v2], Data [id=2, value=v3], Data [id=3, value=v4]]

备注

若Data类不实现Serializable接口,则可通过编译,但运行时会报如下异常:

1
Exception in thread "main" java.io.NotSerializableException: stest.Data
2
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1183)
3
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
4
	at java.util.ArrayList.writeObject(ArrayList.java:742)
5
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
6
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
7
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
8
	at java.lang.reflect.Method.invoke(Method.java:606)
9
	at java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:988)
10
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1495)
11
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1431)
12
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1177)
13
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:347)
14
	at stest.STest.writeByFOS(STest.java:19)
15
	at stest.STest.main(STest.java:57)