友情提示:如果本网页打开太慢或显示不完整,请尝试鼠标右键“刷新”本网页!
第三电子书 返回本书目录 加入书签 我的书架 我的书签 TXT全本下载 『收藏到我的浏览器』

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

快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部! 如果本书没有阅读完,想下次继续接着阅读,可使用上方 "收藏到我的浏览器" 功能 和 "加入书签" 功能!


f 指出“我准备向你提供文件名”。若省略此参数,jar 会假定它的输入来自标准输入;或者在它创建文件 

时,输出会进入标准输出内  

m 指出第一个参数将是用户自建的详情表文件的名字  

v 产生详细输出,对 jar做的工作进行巨细无遗的描述  

O 只保存文件;不压缩文件(用于创建一个 JAR 文件,以便我们将其置入自己的类路径中)  

M 不自动生成详情表文件  

  

在准备进入 JAR 文件的文件中,若包括了一个子目录,那个子目录会自动添加,其中包括它自己的所有子目 

录,以此类推。路径信息也会得到保留。  

下面是调用 jar 的一些典型方法:  

  

jar cf myJarFile。jar *。class  

用于创建一个名为myJarFile。jar 的JAR 文件,其中包含了当前目录中的所有类文件,同时还有自动产生的 

详情表文件。  

  

jar cmf myJarFile。jar myManifestFile。mf *。class  

与前例类似,但添加了一个名为myManifestFile。mf 的用户自建详情表文件。  

  

jar tf myJarFile。jar  

生成myJarFile。jar 内所有文件的一个目录表。  

  

jar tvf myJarFile。jar  

添加“verbose”(详尽)标志,提供与myJarFile。jar 中的文件有关的、更详细的资料。  

  

jar cvf myApp。jar audio classes image  

假定audio,classes 和 image 是子目录,这样便将所有子目录合并到文件myApp。jar 中。其中也包括了 

 “verbose”标志,可在jar 程序工作时反馈更详尽的信息。  

  

如果用O 选项创建了一个JAR 文件,那个文件就可置入自己的类路径(CLASSPATH)中:  

CLASSPATH=〃lib1。jar;lib2。jar;〃  

Java 能在 lib1。jar 和 lib2。jar 中搜索目标类文件。  

  

jar工具的功能没有zip 工具那么丰富。例如,不能够添加或更新一个现成 JAR 文件中的文件,只能从头开 

始新建一个 JAR 文件。此外,不能将文件移入一个 JAR 文件,并在移动后将它们删除。然而,在一种平台上 

创建的 JAR 文件可在其他任何平台上由jar工具毫无阻碍地读出(这个问题有时会困扰zip 工具)。  

正如大家在第 13 章会看到的那样,我们也用JAR 为Java Beans 打包。  



10。9 对象序列化  



Java 1。1 增添了一种有趣的特性,名为“对象序列化”(Object Serialization)。它面向那些实现了 

Serializable 接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子。这一过程亦可 

通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。换句话说,可以先在Windows 机器上创 

建一个对象,对其序列化,然后通过网络发给一台 Unix 机器,然后在那里准确无误地重新“装配”。不必关 

心数据在不同机器上如何表示,也不必关心字节的顺序或者其他任何细节。  

就其本身来说,对象的序列化是非常有趣的,因为利用它可以实现“有限持久化”。请记住“持久化”意味 

着对象的“生存时间”并不取决于程序是否正在执行——它存在或“生存”于程序的每一次调用之间。通过 

序列化一个对象,将其写入磁盘,以后在程序重新调用时重新恢复那个对象,就能圆满实现一种“持久”效 

果。之所以称其为“有限”,是因为不能用某种“persistent”(持久)关键字简单地地定义一个对象,并 

让系统自动照看其他所有细节问题(尽管将来可能成为现实)。相反,必须在自己的程序中明确地序列化和 

组装对象。  

语言里增加了对象序列化的概念后,可提供对两种主要特性的支持。Java 1。1 的“远程方法调用”(RMI) 



                                                                           315 


…………………………………………………………Page 317……………………………………………………………

使本来存在于其他机器的对象可以表现出好象就在本地机器上的行为。将消息发给远程对象时,需要通过对 

象序列化来传输参数和返回值。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 (除非
返回目录 上一页 下一页 回到顶部 1 1
快捷操作: 按键盘上方向键 ← 或 → 可快速上下翻页 按键盘上的 Enter 键可回到本书目录页 按键盘上方向键 ↑ 可回到本页顶部!
温馨提示: 温看小说的同时发表评论,说出自己的看法和其它小伙伴们分享也不错哦!发表书评还可以获得积分和经验奖励,认真写原创书评 被采纳为精评可以获得大量金币、积分和经验奖励哦!