程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> java的對象串聯

java的對象串聯

編輯:關於JAVA

Java 1.1增添了一種有趣的特性,名為“對象序列化”(Object Serialization)。它面向那些實現了Serializable接口的對象,可將它們轉換成一系列字節,並可在以後完全恢復回原來的樣子。這一過程亦可通過網絡進行。這意味著序列化機制能自動補償操作系統間的差異。換句話說,可以先在Windows機器上創建一個對象,對其序列化,然後通過網絡發給一台Unix機器,然後在那裡准確無誤地重新“裝配”。不必關心數據在不同機器上如何表示,也不必關心字節的順序或者其他任何細節。
就其本身來說,對象的序列化是非常有趣的,因為利用它可以實現“有限持久化”。請記住“持久化”意味著對象的“生存時間”並不取決於程序是否正在執行——它存在或“生存”於程序的每一次調用之間。通過序列化一個對象,將其寫入磁盤,以後在程序重新調用時重新恢復那個對象,就能圓滿實現一種“持久”效果。之所以稱其為“有限”,是因為不能用某種“persistent”(持久)關鍵字簡單地地定義一個對象,並讓系統自動照看其他所有細節問題(盡管將來可能成為現實)。相反,必須在自己的程序中明確地序列化和組裝對象。
語言裡增加了對象序列化的概念後,可提供對兩種主要特性的支持。Java 1.1的“遠程方法調用”(RMI)使本來存在於其他機器的對象可以表現出好象就在本地機器上的行為。將消息發給遠程對象時,需要通過對象序列化來傳輸參數和返回值。RMI將在第15章作具體討論。
對象的序列化也是Java Beans必需的,後者由Java 1.1引入。使用一個Bean時,它的狀態信息通常在設計期間配置好。程序啟動以後,這種狀態信息必須保存下來,以便程序啟動以後恢復;具體工作由對象序列化完成。
對象的序列化處理非常簡單,只需對象實現了Serializable接口即可(該接口僅是一個標記,沒有方法)。在Java 1.1中,許多標准庫類都發生了改變,以便能夠序列化——其中包括用於基本數據類型的全部封裝器、所有集合類以及其他許多東西。甚至Class對象也可以序列化(第11章講述了具體實現過程)。
為序列化一個對象,首先要創建某些OutputStream對象,然後將其封裝到ObjectOutputStream對象內。此時,只需調用writeObject()即可完成對象的序列化,並將其發送給OutputStream。相反的過程是將一個InputStream封裝到ObjectInputStream內,然後調用readObject()。和往常一樣,我們最後獲得的是指向一個上溯造型Object的句柄,所以必須下溯造型,以便能夠直接設置。
對象序列化特別“聰明”的一個地方是它不僅保存了對象的“全景圖”,而且能追蹤對象內包含的所有句柄並保存那些對象;接著又能對每個對象內包含的句柄進行追蹤;以此類推。我們有時將這種情況稱為“對象網”,單個對象可與之建立連接。而且它還包含了對象的句柄數組以及成員對象。若必須自行操縱一套對象序列化機制,那麼在代碼裡追蹤所有這些鏈接時可能會顯得非常麻煩。在另一方面,由於Java對象的序列化似乎找不出什麼缺點,所以請盡量不要自己動手,讓它用優化的算法自動維護整個對象網。下面這個例子對序列化機制進行了測試。它建立了許多鏈接對象的一個“Worm”(蠕蟲),每個對象都與Worm中的下一段鏈接,同時又與屬於不同類(Data)的對象句柄數組鏈接:
 

//: Worm.java
// Demonstrates object serialization in Java 1.1
import java.io.*;

class Data implements Serializable {
  private int i;
  Data(int x) { i = x; }
  public String toString() {
    return Integer.toString(i);
  }
}

public class Worm implements Serializable {
  // Generate a random int value:
  private static int r() {
    return (int)(Math.random() * 10);
  }
  private Data[] d = {
    new Data(r()), new Data(r()), new Data(r())
  };
  private Worm next;
  private char c;
  // Value of i == number of segments
  Worm(int i, char x) {
    System.out.println(" Worm constructor: " + i);
    c = x;
    if(--i > 0)
      next = new Worm(i, (char)(x + 1));
  }
  Worm() {
    System.out.println("Default constructor");
  }
  public String toString() {
    String s = ":" + c + "(";
    for(int i = 0; i < d.length; i++)
      s += d[i].toString();
    s += ")";
    if(next != null)
      s += next.toString();
    return s;
  }
  public static void main(String[] args) {
    Worm w = new Worm(6, 'a');
    System.out.println("w = " + w);
    try {
      ObjectOutputStream out =
        new ObjectOutputStream(
          new FileOutputStream("worm.out"));
      out.writeObject("Worm storage");
      out.writeObject(w);
      out.close(); // Also flushes output
      ObjectInputStream in =
        new ObjectInputStream(
          new FileInputStream("worm.out"));
      String s = (String)in.readObject();
      Worm w2 = (Worm)in.readObject();
      System.out.println(s + ", w2 = " + w2);
    } catch(Exception e) {
      e.printStackTrace();
    }
    try {
      ByteArrayOutputStream bout =
        new ByteArrayOutputStream();
      ObjectOutputStream out =
        new ObjectOutputStream(bout);
      out.writeObject("Worm storage");
      out.writeObject(w);
      out.flush();
      ObjectInputStream in =
        new ObjectInputStream(
          new ByteArrayInputStream(
            bout.toByteArray()));
      String s = (String)in.readObject();
      Worm w3 = (Worm)in.readObject();
      System.out.println(s + ", w3 = " + w3);
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
} ///:~

更有趣的是,Worm內的Data對象數組是用隨機數字初始化的(這樣便不用懷疑編譯器保留了某種原始信息)。每個Worm段都用一個Char標記。這個Char是在重復生成鏈接的Worm列表時自動產生的。創建一個Worm時,需告訴構建器希望它有多長。為產生下一個句柄(next),它總是用減去1的長度來調用Worm構建器。最後一個next句柄則保持為null(空),表示已抵達Worm的尾部。
上面的所有操作都是為了加深事情的復雜程度,加大對象序列化的難度。然而,真正的序列化過程卻是非常簡單的。一旦從另外某個流裡創建了ObjectOutputStream,writeObject()就會序列化對象。注意也可以為一個String調用writeObject()。亦可使用與DataOutputStream相同的方法寫入所有基本數據類型(它們有相同的接口)。
有兩個單獨的try塊看起來是類似的。第一個讀寫的是文件,而另一個讀寫的是一個ByteArray(字節數組)。可利用對任何DataInputStream或者DataOutputStream的序列化來讀寫特定的對象;正如在關於連網的那一章會講到的那樣,這些對象甚至包括網絡。一次循環後的輸出結果如下:
 

Worm constructor: 6
Worm constructor: 5
Worm constructor: 4
Worm constructor: 3
Worm constructor: 2
Worm constructor: 1
w = :a(262):b(100):c(396):d(480):e(316):f(398)
Worm storage, w2 = :a(262):b(100):c(396):d(480):e(316):f(398)
Worm storage, w3 = :a(262):b(100):c(396):d(480):e(316):f(398)

可以看出,裝配回原狀的對象確實包含了原來那個對象裡包含的所有鏈接。
注意在對一個Serializable(可序列化)對象進行重新裝配的過程中,不會調用任何構建器(甚至默認構建器)。整個對象都是通過從InputStream中取得數據恢復的。
作為Java 1.1特性的一種,我們注意到對象的序列化並不屬於新的Reader和Writer層次結構的一部分,而是沿用老式的InputStream和OutputStream結構。所以在一些特殊的場合下,不得不混合使用兩種類型的層次結構。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved