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

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

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



      int c;  

      while((c = in。read()) != …1)  

        out。write(c);  

      in。close();  

      out。close();  

      System。out。println(〃Reading file〃);  

      BufferedReader in2 =  

        new BufferedReader(  

          new InputStreamReader(  

            new GZIPInputStream(  

              new FileInputStream(〃test。gz〃))));  

      String s;  

      while((s = in2。readLine()) != null)  

        System。out。println(s);  

    } catch(Exception e) {  

      e。printStackTrace();  

    }  

  }  

} ///:~  

  

压缩类的用法非常直观——只需将输出流封装到一个GZIPOutputStream 或者ZipOutputStream 内,并将输入 

流封装到GZIPInputStream 或者ZipInputStream 内即可。剩余的全部操作就是标准的IO读写。然而,这是 

一个很典型的例子,我们不得不混合使用新旧 IO流:数据的输入使用Reader 类,而 GZIPOutputStream 的构 

建器只能接收一个 OutputStream 对象,不能接收Writer 对象。  



10。8。2 用 Zip 进行多文件保存  



提供了 Zip 支持的 Java 1。1 库显得更加全面。利用它可以方便地保存多个文件。甚至有一个独立的类来简化 

对Zip 文件的读操作。这个库采采用的是标准Zip 格式,所以能与当前因特网上使用的大量压缩、解压工具 

很好地协作。下面这个例子采取了与前例相同的形式,但能根据我们需要控制任意数量的命令行参数。除此 

之外,它展示了如何用 Checksum 类来计算和校验文件的“校验和”(Checksum )。可选用两种类型的 

Checksum :Adler32 (速度要快一些)和CRC32 (慢一些,但更准确)。  

  

//: Zippress。java  

// Uses Java 1。1 Zip pression to press  

// any number of files whose names are passed  

// on the mand line。  

import java。io。*;  

import java。util。*;  



                                                                                          312 


…………………………………………………………Page 314……………………………………………………………

import java。util。zip。*;  

  

public class Zippress {  

  public static void main(String'' args) {  

    try {  

      FileOutputStream f =  

        new FileOutputStream(〃test。zip〃);  

      CheckedOutputStream csum =  

        new CheckedOutputStream(  

          f; new Adler32());  

      ZipOutputStream out =  

        new ZipOutputStream(  

          new BufferedOutputStream(csum));  

      out。setment(〃A test of Java Zipping〃);  

      // Can't read the above ment; though  

      for(int i = 0; i 《 args。length; i++) {  

        System。out。println(  

          〃Writing file 〃 + args'i');  

        BufferedReader in =  

          new BufferedReader(  

            new FileReader(args'i'));  

        out。putNextEntry(new ZipEntry(args'i'));  

        int c;  

        while((c = in。read()) != …1)  

          out。write(c);  

        in。close();  

      }  

      out。close();  

      // Checksum valid only after the file  

      // has been closed!  

      System。out。println(〃Checksum: 〃 +  

        csum。getChecksum()。getValue());  

      // Now extract the files:  

      System。out。println(〃Reading file〃);  

      FileInputStream fi =  

         new FileInputStream(〃test。zip〃);  

      CheckedInputStream csumi =  

        new CheckedInputStream(  

          fi; new Adler32());  

      ZipInputStream in2 =  

        new ZipInputStream(  

          new BufferedInputStream(csumi));  

      ZipEntry ze;  

      System。out。println(〃Checksum: 〃 +  

        csumi。getChecksum()。getValue());  

      while((ze = in2。getNextEntry()) != null) {  

        System。out。println(〃Reading file 〃 + ze);  

        int x;  

        while((x = in2。read()) != …1)  

          System。out。write(x);  

      }  

      in2。close();  



                                                                                          313 


…………………………………………………………Page 315……………………………………………………………

      // Alternative way to open and read  

      // zip files:  

      ZipFile zf = new ZipFile(〃test。zip〃);  

      Enumeration e = zf。entries();  

      while(e。hasMoreElements()) {  

        ZipEntry ze2 = (ZipEntry)e。nextElement();  

        System。out。println(〃File: 〃 + ze2);  

        // 。。。 and extract the data as before  

      }  

    } catch(Exception e) {  

      e。printStackTrace();  

    }  

  }  

} ///:~  

  

对于要加入压缩档的每一个文件,都必须调用 putNextEntry(),并将其传递给一个ZipEntry 对象。 

ZipEntry 对象包含了一个功能全面的接口,利用它可以获取和设置 Zip 文件内那个特定的 Entry (入口)上 

能够接受的所有数据:名字、压缩后和压缩前的长度、日期、CRC 校验和、额外字段的数据、注释、压缩方 

法以及它是否一个目录入口等等。然而,虽然 Zip 格式提供了设置密码的方法,但Java 的Zip 库没有提供这 

方面的支持。而且尽管 CheckedInputStream 和CheckedOutputStream 同时提供了对Adler32 和CRC32 校验和 

的支持,但是ZipEntry 只支持 CRC 的接口。这虽然属于基层Zip 格式的限制,但却限制了我们使用速度更快 

的Adler32。  

为解压文件,ZipInputStream 提供了一个 getNextEntry()方法,能在有的前提下返回下一个ZipEntry 。作 

为一个更简洁的方法,可以用 ZipFile 对象读取文件。该对象有一个 entries()方法,可以为 ZipEntry 返回 

一个Enumeration (枚举)。  

为读取校验和,必须多少拥有对关联的Checksum 对象的访问权限。在这里保留了指向CheckedOutputStream 

和CheckedInputStream 对象的一个句柄。但是,也可以只占有指向Checksum 对象的一个句柄。  

Zip 流中一个令人困惑的方法是 setment()。正如前面展示的那样,我们可在写一个文件时设置注释内 

容,但却没有办法取出 ZipInputStream 内的注释。看起来,似乎只能通过ZipEntry 逐个入口地提供对注释 

的完全支持。  

当然,使用 GZIP 或Zip 库时并不仅仅限于文件——可以压缩任何东西,包括要通过网络连接发送的数据。  



10。8。3 Java 归档(jar )实用程序  



Zip 格式亦在Java 1。1 的JAR (Java ARchive)文件格式中得到了采用。这种文件格式的作用是将一系列文 

件合并到单个压缩文件里,就象Zip 那样。然而,同Java 中其他任何东西一样,JAR 文件是跨平台的,所以 

不必关心涉及具体平台的问题。除了可以包括声音和图像文件以外,也可以在其中包括类文件。  

涉及因特网应用时,JAR 文件显得特别有用。在 JAR 文件之前,Web 浏览器必须重复多次请求Web 服务器,以 

便下载完构成一个“程序片”(Applet)的所有文件。除此以外,每个文件都是未经压缩的。但在将所有这 

些文件合并到一个 JAR 文件里以后,只需向远程服务器发出一次请求即可。同时,由于采用了压缩技术,所 

以可在更短的时间里获得全部数据。另外,JAR 文件里的每个入口(条目)都可以加上数字化签名(详情参 

考Java 用户文档)。  

一个JAR 文件由一系列采用 Zip 压缩格式的文件构成,同时还有一张“详情单”,对所有这些文件进行了描 

述(可创建自己的详情单文件;否则,jar程序会为我们代劳)。在联机用户文档中,可以找到与JAR 详情 

单更多的资料(详情单的英语是“Manifest ”)。  

jar 实用程序已与Sun 的JDK 配套提供,可以按我们的选择自动压缩文件。请在命令行调用它:  

  

jar '选项' 说明 '详情单' 输入文件  

  

其中,“选项”用一系列字母表示(不必输入连字号或其他任何指示符)。如下所示:  

  

c 创建新的或空的压缩档  

t 列出目录表  



                                                                                 314 


…………………………………………………………Page 316……………………………………………………………

x 解压所有文件  

x file 解压指定文件  

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