八宝书库 > 文学其他电子书 > Java编程思想第4版[中文版](PDF格式) >

第86部分

Java编程思想第4版[中文版](PDF格式)-第86部分

小说: Java编程思想第4版[中文版](PDF格式) 字数: 每页4000字

按键盘上方向键 ← 或 → 可快速上下翻页,按键盘上的 Enter 键可回到本书目录页,按键盘上方向键 ↑ 可回到本页顶部!
————未阅读完?加入书签已便下次继续阅读!




象序列化来传输参数和返回值。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〃);  

  }  



                                                                                         316 


…………………………………………………………Page 318……………………………………………………………

  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 构 



                                                                                          317 


…………………………………………………………Page 319……………………………………………………………

建器。最后一个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 结构。所以在一些特殊的场合下,不得不混合使用两种类型的层 

次结构。  



10。9。1 寻找类  



读者或许会奇怪为什么需要一个对象从它的序列化状态中恢复。举个例子来说,假定我们序列化一个对象, 

并通过网络将其作为文件传送给另一台机器。此时,位于另一台机器的程序可以只用文件目录来重新构造这 

个对象吗?  

回答这个问题的最好方法就是做一个实验。下面这个文件位于本章的子目录下:  

  

//: Alien。java  

// A serializable class  

import java。io。*;  

  

public class Alien implements Serializable {  

} ///:~  

  

用于创建和序列化一个 Alien 对象的文件位于相同的目录下:  

  

//: FreezeAlien。java  

// Create a serialized output file  

import java。io。*;  

  

public class FreezeAlien {  

  public static void main(String'' args)   

      throws Exception {  

    ObjectOutput out =   

      new ObjectOutputStream(  

        new FileOutputStream(〃file。x〃));  

    Alien zorcon = new Alien();  



                                                                                         318 


…………………………………………………………Page 320……………………………………………………………

    out。writeObject(zorcon);   

  }  

} ///:~  

  

该程序并不是捕获和控制违例,而是将违例简单、直接地传递到main()外部,这样便能在命令行报告它们。  

程序编译并运行后,将结果产生的 file。x 复制到名为 xfiles 的子目录,代码如下:  

  

//: ThawAlien。java  

// Try to recover a serialized file without the   

// class of object that's stored in that file。  

package c10。xfiles;  

import java。io。*;  

  

public class ThawAlien {  

  public static void main(String'' args)   

      throws Exception {  

    ObjectInputStream in =  

      new ObjectInputStream(  

        new FileInputStream(〃file。x〃));  

    Object mystery = in。readObject();  

    System。out。println(  

      mystery。getClass()。toString());  

  }  

} ///:~  

  

该程序能打开文件,并成功读取mystery 对象中的内容。然而,一旦尝试查找与对象有关的任何资料——这 

要求Alien 的Class 对象——Java 虚拟机(JVM)便找不到Alien。class (除非它正好在类路径内,而本例理 

应相反)。这样就会得到一个名叫 ClassNotFoundException 的违例(同样地,若非能够校验Alien 存在的证 

据,否则它等于消失)。  

恢复了一个序列化的对象后,如果想对其做更多的事情,必须保证JVM 能在本地类路径或者因特网的其他什 

么地方找到相关的。class文件。  



10。9。2  序列化的控制  



正如大家看到的那样,默认的序列化机制并不难操纵。然而,假若有特殊要求又该怎么办呢?我们可能有特 

殊的安全问题,不希望对象的某一部分序列化;或者某一个子对象完全不必序列化,因为对象恢复以后,那 

一部分需要重新创建。  

此时,通过实现Externalizable 接口,用它代替Serializable 接口,便可控制序列化的具体过程。这个 

Externalizable 接口扩展了 Serializable,并增添了两个方法:writeExternal()和readExternal()。在序 

列化和重新装配的过程中,会自动调用这两个方法,以便我们执行一些特殊操作。  

下面这个例子展示了Externalizable 接口方法的简单应用。注意Blip1 和Blip2 几乎完全一致,除了极微小 

的差别(自己研究一下代码,看看是否能发现):  

  

//: Blips。java  

// Simple use of Externalizable & a pitfall  

import java。io。*;  

import java。util。*;  

  

class Blip1 implements Externalizable {  

  public Blip1() {  

    System。out。println(〃Blip1 Constructor〃);  

  }  

  public void writeExternal(ObjectOutput out)  



                                                                                          319 


…………………………………………………………Page 321……………………………………………………………

      throws IOException {  

    System。out。println(〃Blip1。writeExternal〃);  

  }  

  public void readExternal(ObjectInput in)  

     throws IOException; ClassNotFoundException {  

    System。out。println(〃Blip1。readExternal〃);  

  }  

}  

  

class Blip2 implements Externalizable {  

  Blip2() {  

    System。out。println(〃Blip2 Constructor〃);  

  }  

  public void writeExternal(ObjectOutput out)  

      throws IOException {  

    System。out。println(〃Blip2。writeExternal〃);  

  }  

  public void readExternal(ObjectInput in)  

     throws IOException; ClassNotFoundException {  

    System。out。println(〃

返回目录 上一页 下一页 回到顶部 0 1

你可能喜欢的