《Java编程思想》读书笔记(六)

第十章 Java IO系统

前6小节介绍Java1.0的IO流,之后介绍Java1.1的IO流

输入和输出

与输入有关的所有类都从InputStream继承,与输出有关的所有类都从OutputStream继承

IO流的典型应用

  1. 缓冲的输入文件:
    • 打开一个文件以便输入使用FileInputStream,参数为String或File对象;
    • 为提高速度,对文件进行缓冲处理,使用BufferedInputStream;
    • 为以格式化的形式输入数据,将结果句柄赋给DataInputStream,这是进行读取操作的接口
  2. 从内存输入:
    • StringBufferInputStream,参数为String,而不是StringBuffer;
    • read()依次读取每个字符,返回为int
  3. 格式化内存输入:
    • 将StringBufferInputStream封装到DataInputStream中;
    • readByte()方法每次读取一个字符,且都是有效的,不可用返回值来侦测何时结束输入,可用avilable()方法判断有多少字符可用,或用捕获违例控制数据流(大材小用)
  4. 行的编号与文件输出:
    • LineNumberInputStream跟踪输入行的编号,在其基础上创建一个DataInputStream,以便读入数据;
    • 格式化输出首先创建FileOutputStream,将其与一个文件链接,考虑效率问题,将其传入BufferedOutputStream,为进行格式化将其再转为PrintStream
  5. 如果不为自己的所有输出文件调用close(),就可能发生缓冲区不会得到刷新,造成文件不完整
  6. 保存与恢复数据:为了输出数据,以便由另一个数据流恢复(读取),需使用DataOutputStream写入数据,使用DataInputStream恢复数据
  7. 读写随即访问文件:
    • RandomAccessFile与IO层次结构的剩余部分几乎是完全隔离的,不可将其与InputStream及OutputStream子类的任何部分关联起来;
    • 使用RandomAccessFile的时候,类似于组合使用DataInputStream和DataOutputStream,还可以通过seek()再文件中移动,对某个值作出修改
  8. 从标准输入中读取数据:System.out和System.err为PrintStream对象,System.in为原始的InputStream,也就是说可以直接使用System.out和System.err,但必须事先封装System.in,否则不能从中读取数据,将其封装到一个DataInputStream中
  9. 管道化数据流用于线程间的通信

Java1.1的IO流

  1. Java1.1中添加了Writter和Reader层次;若想使用readLine(),应改用BufferedReader,而不是DataInputStream,但除了这种情况外,DataInputStream仍是Java1.1 IO库的首选成员
  2. Java 1.1在System 类中添加了特殊的方法,允许我们重新定向标准输入、输出以及错误 IO流:setIn(InputStream),setOut(PrintStream),setErr(PrintStream);Java1.1不推荐使用PrintStream构建器

压缩

  1. Java1.1添加一个支持对压缩格式的数据流的读写,属于InputStream和OutputStream层次结构的一部分,而不是从新的Reader和Writer中衍生出来的(可用InputStreamReader和OuputStreamWriter在不同的类型间方便地进行转换)
  2. 压缩类的用法:将输出流封装到一个GZIPOutputStream 或者ZipOutputStream 内,将输入流封装到GZIPInputStream 或者ZipInputStream 内
  3. 多文件压缩保存:
    • 对于要加入压缩档的每一个文件,都必须调用putNextEntry(),并将其传递给一个 ZipEntry 对象;
    • CheckedInputStream 和CheckedOutputStream同时提供了对Adler32 和CRC32 校验和 的支持,但是ZipEntry 只支持 CRC的接口;
    • 为解压文件,ZipInputStream提供了一个 getNextEntry()方法,能在有的前提下返回下一个ZipEntry;
    • 也可以使用ZipFile读取文件,使用entries()方法获取ZipEntry的一个Enumeration(枚举);
    • 使用setCommit()可以在写一个文件时设置注释内容,但却没有办法取出ZipInputStream内的注释;
    • 使用 GZIP 或Zip库可以压缩任何东西,包括要通过网络连接发送的数据
  4. JAR文件是跨平台的,一个JAR文件由一系列采用Zip压缩格式的文件构成,同时还有一张“详情单”,对所有这些文件进行了描述,可以自己创建“详情单”,否则jar程序会自动创建
  5. 在命令行调用jar程序:jar [选项] 说明 [详情单] 输入文件

对象序列化

  1. 对象序列化:面向那些实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子,利用它可实现“有限持久化”
  2. “持久化”意味着对象的生存时间并不取决于程序是否正在执行,它存在或生存于程序的每一次调用之间
  3. 序列化对象:
    • 创建某些OutputStream对象,然后将其封装到ObjectOutputStream 对象内,调用writeObject()即可完成对象的序列化,并将其发送给OutputStream;
    • 相反的过程是是将一个InputStream封装到ObjectInputStream内,然后调用 readObject();
    • 最后得到的是指向上溯造型Object的句柄,必须下溯造型
  4. “序列网”:对象序列化能追踪对象内包含的所有句柄并保存那些对象,接着又能对每个对象内包含的句柄进行追踪,以此类推
  5. 在对一个Serializable(可序列化)对象进行重新装配的过程中,不会调用任何构建器(甚至默认构建器),整个对象都是通过从InputStream中取得数据恢复的
  6. 对象的序列化并不属于新的 Reader和Writer层次结构的一部分,而是沿用老式的InputStream 和OutputStream 结构
  7. 恢复了一个序列化的对象后,如果想对其做更多的事情,必须保证JVM能在本地类路径或者因特网的其他什么地方找到相关的.class文件
  8. 通过实现Externalizable接口,用它代替Serializable接口来控制序列化的具体过程;Externalizable接口扩展了 Serializable,并增添了两个方法:writeExternal()和readExternal()
  9. 恢复一个Serializable时,不调用构建器,但恢复Externalizable对象时,所有普通的默认构建器行为都会发生,包括字段定义时的初始化,而且会调用readExternal()
  10. 不可仅在writeExternal()中写入重要数据,还必须在readExternal()中恢复这些数据
  11. Serializable会将所有信息自动序列化,包括private的部分,而Externalizable不会自动序列化任何东西,需自定义要序列化的部分;在Serializable中可使用transient逐个关闭序列化,transient关键字只能伴随Serializable使用
  12. Externalizable的替代方法:实现Serializable接口,并添加writeObject()和readObject()方法(private方法),一旦对象被序列化或重新装配,会分别调用这两个方法,也就是说,只要提供了这两个方法,就会优先调用它们,而不考虑默认的序列化机制
  13. 只要将所有东西都序列化到单独一个数据流里,就能恢复获得与以前写入时完全一样的对象网
  14. static值的序列化需自定义,Serializable不会将static值自动序列化