Java 序列化详解及简单实现实例

时间:2021-08-17 23:21:41

一、序列化

序列化定义:序列化是将对象状态转换为可保持或传输的格式的过程。与序列化相对的是反序列化,它将流转换为对象。这两个过程结合起来,可以轻松地存储和传输数据。

目的:

  1. 以某种存储形式使自定义对象持久化
  2. 将对象从一个地方传递到另一个地方

二、Java序列化

一个对象能够序列化的前提是实现Serializable接口。Serializable接口没有方法,更像是个标记。有了这个标记的Class就能被序列化机制处理。如下:

?
1
2
class myPoint implements Serializable{
}

JAVA反序列化不会调用任何构造器

序列化的控制:Externalizable。读写都交给你

  1. 要在方法writeExternal写入序列化的参数
  2. 要在方法readExternal读取反序列化的值
  3. 要有默认的构造方法(readExternal执行完成,再执行默认的构造器)
?
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
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException,ClassNotFoundException;
public class Point implements Externalizable {
  private int a;
  private int b;
  public Point(int a, int b) {
    this.a = a;
    this.b = b;
  }
  public Point() {
  }
  public String toString() {
    return a + " , " + b;
  }
   
  public void writeExternal(ObjectOutput out) throws IOException {
    out.write(a);
    out.write(b);
  }
  public void readExternal(ObjectInput in) throws IOException,
      ClassNotFoundException {
    a = in.read();
    b = in.read();
  }
  public static void main(String[] args) throws IOException,
      ClassNotFoundException {
    String file = "d://1.txt";
    Point p = new Point(1, 2);
    System.out.println(p);
    FileOutputStream fos = new FileOutputStream(file);
    ObjectOutputStream oos = new ObjectOutputStream(fos);
    oos.writeObject(p);
    FileInputStream fis = new FileInputStream(file);
    ObjectInputStream ois = new ObjectInputStream(fis);
    Point pp = (Point) ois.readObject();
    System.out.println(pp);
  }
}
  1. transient关键字 关闭序列化自动进行。
  2. 不管你选择了哪种序列化形式,都要为自己编写的每个可序列化的类声明一个显示的序列版本UID(serial version UID)

三、序列化的问题

在effective Java中列举出了java序列化要注意的一些问题:

1.谨慎地设计实现Serializable接口

  1. 实现发布了就是一种承诺
  2. 如果一个类是为继承设计的,在‘允许子类实现Serializable接口'与‘禁止子类实现Serializable接口'取一个折中的方案是:提供一个可访问的无参构造器

2.保护性地编写 readObject()方法,因为readObject()是构建实例的入口。

不保护可能出现 构建了不满足要求的 实例

3.考虑自定义的序列化形式

  1. 逻辑内容 与 物理表示法
  2. 如果一个对象的 ‘物理表示法'等同于它的‘逻辑内容',可能就适用于使用默认的序列化形式。
  3. 如果有更好的 ‘物理表示法'在表示‘逻辑内容'则可以自定义序列化形式。
?
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
public class StringList implements Serializable {
  private transient int size = 0;
  private transient Entity head = null;
  public final void add(String str) {
    // ...
  }
  private static class Entity {
    String data;
    Entity next;
    Entity previous;
  }
  private void writeObject(ObjectOutputStream s) throws IOException {
    s.defaultWriteObject();
    s.write(size);
    for (Entity e = head; e != null; e = e.next) {
      s.writeObject(e.data);
    }
  }
  private void readObject(ObjectInputStream s) throws IOException,
      ClassNotFoundException {
    s.defaultReadObject();
    int num = s.read();
    for (int i = 0; i < num; i++) {
      this.add((String) s.readObject());
    }
  }
}

四、序列化代理模式

    序列化机制提供的钩子函数有:

       writeReplace writeObject  readObject  readResolve

  1. writeReplace:序列化的时候替换所要序列化的对象。
  2. writeObject:写入序列化的对象
  3. readObject:读取序列化的对象
  4. readResolve:最后返回序列化对象
?
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
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.util.Date;
public final class Period implements Serializable {
  private static final long serialVersionUID = 100L;
  private final Date start;
  private final Date end;
  public Period(Date start, Date end) {
    this.start = new Date(start.getTime());
    this.end = new Date(end.getTime());
    if (this.start.compareTo(this.end) > 0) {
      throw new IllegalArgumentException(start + " after " + end);
    }
  }
  public Date start() {
    return new Date(start.getTime());
  }
  public Date end() {
    return new Date(end.getTime());
  }
  public String toString() {
    return start + " - " + end;
  }
  // 不给
  private Object writeReplace() {
    return new SerializationProxy(this);
  }
  private void readObject(ObjectInputStream stream)
      throws InvalidObjectException {
    throw new InvalidObjectException("proxy request");
  }
  private static class SerializationProxy implements Serializable {
    private final Date start;
    private final Date end;
    SerializationProxy(Period p) {
      this.start = p.start;
      this.end = p.end;
    }
    private Object readResolve() {
      return new Period(start, end);
    }
    private static final long serialVersionUID = 1000L;
  }

五、序列化算法

  1. 将对象实例相关的类元数据输出。
  2. 递归地输出类的超类描述直到不再有超类。
  3. 类元数据完了以后,开始从最顶层的超类开始输出对象实例的实际数据值。
  4. 从上至下递归输出实例的数据

 感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

原文链接:http://blog.csdn.net/qq_35101189/article/details/60570096