天天看點

java序列化一、序列化二、Java序列化三、序列化的問題四、序列化代理模式五、序列化算法六、參考文獻

一、序列化

  • 序列化定義:序列化是将對象狀态轉換為可保持或傳輸的格式的過程。與序列化相對的是反序列化,它将流轉換為對象。這兩個過程結合起來,可以輕松地存儲和傳輸資料。
  • 目的:
    • 以某種存儲形式使自定義對象持久化
    • 将對象從一個地方傳遞到另一個地方

二、Java序列化

  • 一個對象能夠序列化的前提是實作Serializable接口。Serializable接口沒有方法,更像是個标記。有了這個标記的Class就能被序列化機制處理。如下:
  • class myPoint implements Serializable{

    }

  • JAVA反序列化不會調用任何構造器
  • 序列化的控制:Externalizable。讀寫都交給你
    • 要在方法writeExternal寫入序列化的參數
    • 要在方法readExternal讀取反序列化的值
    • 要有預設的構造方法(readExternal執行完成,再執行預設的構造器)
    • 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);

      }

      }

    • transient關鍵字 關閉序列化自動進行。
    • 不管你選擇了哪種序列化形式,都要為自己編寫的每個可序列化的類聲明一個顯示的序列版本UID(serial version UID)

三、序列化的問題

在effective java中列舉出了java序列化要注意的一些問題:

  1. 謹慎地設計實作Serializable接口
    • 實作釋出了就是一種承諾
    • 如果一個類是為繼承設計的,在‘允許子類實作Serializable接口’與‘禁止子類實作Serializable接口’取一個折中的方案是:提供一個可通路的無參構造器
  2. 保護性地編寫 readObject()方法,因為readObject()是建構執行個體的入口。
    • 不保護可能出現 建構了不滿足要求的 執行個體
  3. 考慮自定義的序列化形式
    • 邏輯内容 與 實體表示法
    • 如果一個對象的 ‘實體表示法’等同于它的‘邏輯内容’,可能就适用于使用預設的序列化形式。
    • 如果有更好的 ‘實體表示法’在表示‘邏輯内容’則可以自定義序列化形式。
    • 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

  • writeReplace:序列化的時候替換所要序列化的對象。
  • writeObject:寫入序列化的對象
  • readObject:讀取序列化的對象
  • readResolve:最後傳回序列化對象
  • 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://longdick.javaeye.com/blog/458557

六、參考文獻

  • javatips's blog,The Java serialization algorithm revealed: http://www.javaworld.com/community/node/2915 
  • Blog ,Java 序列化算法透析:http://longdick.javaeye.com/blog/458557
  • Java Object Serialization Specification:http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf
  • java程式設計思想 http://www.bookfm.com/reader/online.html?bookid=123482&page=1