上周学习设计模式的时候,了解到Java中的IO使用了装饰者模式,由于装饰者的缺点之一就是会造成有大量的类,让使用者一时无法理解如何使用,Java IO完美继承了这些缺点,每次使用都各种new,非常懵逼,甚至一度觉得要记住太困难了,偶尔使用只能去复制粘贴,不过理解了装饰者模式之后,这事就好办多了,趁机从头理解下Java IO。
编程语言的I/O类库中常常使用流这个抽象概念,它代表任何有能力产出数据的数据源对象或者任何有能力接收数据的接收端对象,“流”屏蔽了实际的I/O设备中处理数据的细节。 –《Thinking in java》
Java的IO分为输入流和输出流两部分,其中输入流的的核心类是 InputStream
和 Reader
,它们都含有 read()
方法,用来读取单个字节或者字节数组,这两个都是抽象类,我们一般不会直接使用。每一种数据源都有相对应的 InputStream
的子类,另外 FilterInputStream
也是 InputStream
的子类,但是它为装饰器类提供基类。同样的,输出流的核心类是 OutputStream
和 Writer
,FilterOututStream
为装饰器类提供基类。
Java IO使用装饰者模式的原因是因为需要多种不同功能的组合,它可以很方便的扩展接口,但是它同时也增加了代码的复杂性,IO类库操作不方便的原因在于必须创建很多“核心”的类,加上所有的装饰器,才得到最终我们想要的单个对象。
按照核心类型的子类和装饰器类进行分类,输入流可以分成如下两类:
-
直接继承
InputStream
的子类,对应每一种类型的数据源类 功能 ByteArrayInputStream 允许将内存的缓冲区当作输入流使用 StringBufferInputStream 将String转换成输入流使用 FileInputStream 从文件中读取信息 PipedInputStream 管道操作相关的输入流 SequenceInputStream 将多个输入流对象合并成一个输入流 FilterInputStream 抽象类,作为“装饰器”的父类 -
直接继承
FilterInputStream
的子类,对应各种装饰器类 功能 DataInputStream 与 DataOutputStream 搭配使用,包含读取基本类型数据的全部接口 BufferedInputStream 使用它可以防止每次读取时都进行实际的写操作,代表使用缓冲区 LineNumberInputStream 跟踪输入流中的行号 PushbackInputStream 作为编译器的扫描器,我们使用不到
同样,输出流也可以分为两类:
-
直接继承
OutputStream
的子类,对应每一种类型的数据源类 功能 ByteArrayOutputStream 在内存中创建缓冲区,将输入流的数据放在这个缓冲区 FileOutputStream 将数据写入文件 PipedOutputStream 管道操作相关的输入流 FilterOutputStream 抽象类,作为“装饰器”的父类 -
直接继承
FilterOutputStream
的子类,对应各种装饰器类 功能 DataOutputStream 与 DataInputStream 搭配使用,包含写入基本类型数据的全部接口 PrintStream 用于产生格式化输出,我们经常使用的 System.out 就是此类型 BufferedOutputStream 使用它可以防止每次发送数据时都进行实际的写操作,代表使用缓冲区,可调用 flush() 清空缓冲区
Java 从 1.1 版本开始引入了 Reader
和 Writer
,InputStream
和 OutputStream
只支持8位的字节流,并且不能很好的处理16位的 Unicode 字符,所以新引入的 Reader
和 Writer
是为了支持字符流,并且速度更快。同样它们也有对应的各种类型子类和装饰器基类 FilterReader
和 FilterWriter
,这里就不展开了,可以参考 jdk 文档。
几乎所有的原始的IO类库都有相对应的 Reader
和 Writer
类来提供天然的Unicode操作,但是有些场合必须使用面向字节的原始类库,所以最明智的做法是尽量尝试使用 Reader
和 Writer
,如果编译不成功,则换回原始类库。
暂时就到这,Java IO这块的内容还很多,后续再接着学习。