第十章 Java IO系统
前6小节介绍Java1.0的IO流,之后介绍Java1.1的IO流
输入和输出
与输入有关的所有类都从InputStream继承,与输出有关的所有类都从OutputStream继承
IO流的典型应用
- 缓冲的输入文件:
- 打开一个文件以便输入使用FileInputStream,参数为String或File对象;
- 为提高速度,对文件进行缓冲处理,使用BufferedInputStream;
- 为以格式化的形式输入数据,将结果句柄赋给DataInputStream,这是进行读取操作的接口
- 从内存输入:
- StringBufferInputStream,参数为String,而不是StringBuffer;
- read()依次读取每个字符,返回为int
- 格式化内存输入:
- 将StringBufferInputStream封装到DataInputStream中;
- readByte()方法每次读取一个字符,且都是有效的,不可用返回值来侦测何时结束输入,可用avilable()方法判断有多少字符可用,或用捕获违例控制数据流(大材小用)
- 行的编号与文件输出:
- LineNumberInputStream跟踪输入行的编号,在其基础上创建一个DataInputStream,以便读入数据;
- 格式化输出首先创建FileOutputStream,将其与一个文件链接,考虑效率问题,将其传入BufferedOutputStream,为进行格式化将其再转为PrintStream
- 如果不为自己的所有输出文件调用close(),就可能发生缓冲区不会得到刷新,造成文件不完整
- 保存与恢复数据:为了输出数据,以便由另一个数据流恢复(读取),需使用DataOutputStream写入数据,使用DataInputStream恢复数据
- 读写随即访问文件:
- RandomAccessFile与IO层次结构的剩余部分几乎是完全隔离的,不可将其与InputStream及OutputStream子类的任何部分关联起来;
- 使用RandomAccessFile的时候,类似于组合使用DataInputStream和DataOutputStream,还可以通过seek()再文件中移动,对某个值作出修改
- 从标准输入中读取数据:System.out和System.err为PrintStream对象,System.in为原始的InputStream,也就是说可以直接使用System.out和System.err,但必须事先封装System.in,否则不能从中读取数据,将其封装到一个DataInputStream中
- 管道化数据流用于线程间的通信
Java1.1的IO流
- Java1.1中添加了Writter和Reader层次;若想使用readLine(),应改用BufferedReader,而不是DataInputStream,但除了这种情况外,DataInputStream仍是Java1.1 IO库的首选成员
- Java 1.1在System 类中添加了特殊的方法,允许我们重新定向标准输入、输出以及错误 IO流:setIn(InputStream),setOut(PrintStream),setErr(PrintStream);Java1.1不推荐使用PrintStream构建器
压缩
- Java1.1添加一个支持对压缩格式的数据流的读写,属于InputStream和OutputStream层次结构的一部分,而不是从新的Reader和Writer中衍生出来的(可用InputStreamReader和OuputStreamWriter在不同的类型间方便地进行转换)
- 压缩类的用法:将输出流封装到一个GZIPOutputStream 或者ZipOutputStream 内,将输入流封装到GZIPInputStream 或者ZipInputStream 内
- 多文件压缩保存:
- 对于要加入压缩档的每一个文件,都必须调用putNextEntry(),并将其传递给一个 ZipEntry 对象;
- CheckedInputStream 和CheckedOutputStream同时提供了对Adler32 和CRC32 校验和 的支持,但是ZipEntry 只支持 CRC的接口;
- 为解压文件,ZipInputStream提供了一个 getNextEntry()方法,能在有的前提下返回下一个ZipEntry;
- 也可以使用ZipFile读取文件,使用entries()方法获取ZipEntry的一个Enumeration(枚举);
- 使用setCommit()可以在写一个文件时设置注释内容,但却没有办法取出ZipInputStream内的注释;
- 使用 GZIP 或Zip库可以压缩任何东西,包括要通过网络连接发送的数据
- JAR文件是跨平台的,一个JAR文件由一系列采用Zip压缩格式的文件构成,同时还有一张“详情单”,对所有这些文件进行了描述,可以自己创建“详情单”,否则jar程序会自动创建
- 在命令行调用jar程序:jar [选项] 说明 [详情单] 输入文件
对象序列化
- 对象序列化:面向那些实现了Serializable接口的对象,可将它们转换成一系列字节,并可在以后完全恢复回原来的样子,利用它可实现“有限持久化”
- “持久化”意味着对象的生存时间并不取决于程序是否正在执行,它存在或生存于程序的每一次调用之间
- 序列化对象:
- 创建某些OutputStream对象,然后将其封装到ObjectOutputStream 对象内,调用writeObject()即可完成对象的序列化,并将其发送给OutputStream;
- 相反的过程是是将一个InputStream封装到ObjectInputStream内,然后调用 readObject();
- 最后得到的是指向上溯造型Object的句柄,必须下溯造型
- “序列网”:对象序列化能追踪对象内包含的所有句柄并保存那些对象,接着又能对每个对象内包含的句柄进行追踪,以此类推
- 在对一个Serializable(可序列化)对象进行重新装配的过程中,不会调用任何构建器(甚至默认构建器),整个对象都是通过从InputStream中取得数据恢复的
- 对象的序列化并不属于新的 Reader和Writer层次结构的一部分,而是沿用老式的InputStream 和OutputStream 结构
- 恢复了一个序列化的对象后,如果想对其做更多的事情,必须保证JVM能在本地类路径或者因特网的其他什么地方找到相关的.class文件
- 通过实现Externalizable接口,用它代替Serializable接口来控制序列化的具体过程;Externalizable接口扩展了 Serializable,并增添了两个方法:writeExternal()和readExternal()
- 恢复一个Serializable时,不调用构建器,但恢复Externalizable对象时,所有普通的默认构建器行为都会发生,包括字段定义时的初始化,而且会调用readExternal()
- 不可仅在writeExternal()中写入重要数据,还必须在readExternal()中恢复这些数据
- Serializable会将所有信息自动序列化,包括private的部分,而Externalizable不会自动序列化任何东西,需自定义要序列化的部分;在Serializable中可使用transient逐个关闭序列化,transient关键字只能伴随Serializable使用
- Externalizable的替代方法:实现Serializable接口,并添加writeObject()和readObject()方法(private方法),一旦对象被序列化或重新装配,会分别调用这两个方法,也就是说,只要提供了这两个方法,就会优先调用它们,而不考虑默认的序列化机制
- 只要将所有东西都序列化到单独一个数据流里,就能恢复获得与以前写入时完全一样的对象网
- static值的序列化需自定义,Serializable不会将static值自动序列化