I/O 就是输入/输出。使我们获取和持久化数据所需要用到的方式。比如从文件中获取内容到内存中,物理设备可能会把硬盘上对应的文件内容加载到内存中。java的I/O类库是采用流的概念来描述这些能够产生数据的源对象和能够接受数据的接受端对象的,以屏蔽物理设备上的操作。
按照概念我们就能够知道流分为输入和输出两部分。查看jdk文档就会发现所有的输入流都是通过继承Inputstream和Reader而来的,都含有read()方法,用于读取。相对的所有的输出流都继承了OutputStream或Writer,含有write()方法,用于写。虽然java当初本想把I/O类库设计的尽量简化,但是为了对付不同的输入输出点,java中通常需要叠合多个对象来提供所期望的功能。因此使用java流,我们很容易陷入迷惑,为了能够清晰准确的使用java流,我们有必要根据这些类的功能对他们进行分类。
从传输的数据上来说主要分为了字节8bit和字符16bit(Unitcode编码就是字符的)。
字节类型的流
InputStream类型
是用来表示那些从不同的数据源产生出入的类,这些数据源包括了:字节数组,String对象,文件,"管道"(多线程中会用到的概念,类似于实际的管道,从一段输入从另一端输出。)其他种类的流组成的序列(我们来把他们合并成一个流),其他的数据源,如Internet连接等等。每一种数据源都有一个相应的InputStream子类。如下:
类 | 功能 | 构造器 | 使用方式 |
ByteArray-InputStream | 允许将内存的缓冲区当做InputStream使用 | ByteArrayInputStream(byte[] buffer) | 作为一种数据源,与修饰器连接之后提供有用的接口 |
StringBuffer-InputStream | 将String转换成InputSteam | StringBufferInputStream(String s) | 同上 |
File-InputStream | 用于从文件中读取信息。 | FileInputStream(File f or String fileName) | 使用方式同上 |
Piped-InputStream | 产生用于写入PipedOutput-Stream的数据 | PipedInputStream | 作为多线程中的数据源,也是按照上面的方式使用 |
Sequence-InputStream | 将两个或多个InputStream转换成单一InputStream | 两个InputStream对象或一个容纳InputStream的Enumeration | 使用方式同上 |
OutputStream类型
与InputStream全部都是对应的,就不一一列出了。
使用修饰器模式添加属性和有用的接口
上面在使用方式中都只提到了一句话,与修饰器进行连接,这个修饰器呢可以抽象为一种模式。描述了利用层叠的数个对象为单个对象动态的和透明的添加职责的方式。
这也正是javaI/O类库中Filter存在的原因。其是所有修饰类的基类。很善于解决通过使用继承的方式会导致大量的,用以满足各种需求的可能组合的类的情况。
修饰器模式也存在一个缺点,它会使我们的程序变得复杂,使我们操作I/O时必须使用核心类,加入所有需要的修饰器才能够得到我们真正想要的那个类。
FilterInputStream和FilterOutputStream是用于控制特定输入输出流的两个类。是InputStream的子类,这两个类是修饰器的必要条件。下面就来介绍这个Filter。
FilterInputStream
它能够完成两件完全不同的事情,DataInputStream允许我们读取不同的基本类型数据以及String对象(所有方法都以"read"开头,例如readByte())。搭配DataOutputStream,能够通过数据流将基本类型的数据进行迁移。至于从哪儿迁到哪儿就是由上面说的各种数据源实现的InputStream和OutputSream决定的了。
其他的类则会在内部修改InputStream的行为方式,比如缓冲或未缓冲,是否保留它所读过的行等等,下面列出了各种修饰器的详细信息。
类 | 功能 | 构造器 | 使用方式 |
Data-InputStream | 与DataOutputStream搭配使用,进行基本数据类型和String类型的转移 | InputStream | 包含了用于读取基本类型数据的全部接口 |
Buffered-InputStream | 使用缓存,可以防止每次读取时都进行实际的写操作 | InputStream,缓冲区大小 | 与接口对象搭配使用,提供缓冲区 |
LineNumber-InputStream | 跟踪输入流中的行数 | InputStream | 与其他接口对象搭配使用,提供了getLineNumber()方法来得到行号 |
Pushback-InputStream | InputStream | 供给编译器调用,编程基本不会使用 |
对应FilterInputStream也有专门向OutputStream写入的修饰器。他与读取的有些不同,因此这里也列出了详细的列表
类 | 功能 | 构造器 | 使用方式 |
Data-OutputStream | 与DataInputStream搭配使用,向流中写入基本类型数据。 | OutputStream | 包含了用于写入基本类型数据的全部接口,比如writeByte(),writeFloat()等 |
PrintStream | 用于产生格式化输出,DataOutput处理数据存储,PrintStream处理显示 | OutputStream,是否每次换行清空缓存 | 用于输出显示,System.out得到的就是这个对象,使用它可能会引起问题,因为它并没有完全的国际化,同时它还捕获了所有的I/O异常。 |
Buffered-OutputStream | 写操作的时候使用缓存区,可以调用flush()清空缓存 | OutputStream,缓存区大小 | 只是提供缓存区,与接口搭配使用 |
上面是字节流的相关内容,下面是字符流
在java1.1中加入了Reader和Writer类,他们提供了兼容Unicode与面向字符的I/O的功能。他们加入的意义主要是为了国际化。
有时我们需要把来自于“字节”层次结构中的类和“字符”层次结构中的类结合起来使用。为了实现这个目的,需要用到adapter(适配器)类:InputStreamReader可以把InputStram转化为Reader.而OutputStreamWriter能把OutputStream转化为Writer.
与InputStream根据数据源的不同有不同的实现相一致的字符流也是如此,他们的对应关系如下:
类型 | 字节流 | 字符流 |
文件 | FileInputStream,FileOutputStream | FileReader,FileWriter |
内存:数组 | ByteArrayInputStream,OutputStream | CharArrayReader,CharArrayWriter |
内存:字符串 | StringBufferInputStream,N/A | StringReader,StringWriter |
管道 | PipedInputStream,PipedOutputStream | PipedReader,PipedWriter |
更改“流”的行为。
对于Stream,为了满足需要,会用FilterInputStream的修饰器子类来修改流。Reader和Writer也沿用了相同的思想。对应关系如下:
字节流 | 字符流 | 说明 |
FilterInputStream,OutputStream | FilterReader,FilterWriter | |
BufferedInputStream,BufferedOutputStream | BufferedReader,BufferedWriter | 与字节流不同的,前面的Reader并不是FilterReader的子类,但是使用方式是基本相同的 |
DataInputStream | 也是用DataInputStream | 如果需要readLine()时,改为使用BufferReader() |
PrintStream | PrintWriter | 二者的使用没有什么区别,只是Writer支持国际化 |
这里还有一个相对独立的类RandomAccessFile.
它拥有和其他I/O类型本质不同的行为,并不是InputStream集成体系中的一个类。它能够在一个文件内向前和向后移动,看起来类似于把DataInputStream和DataOutputStream结合起来使用。
它还支持搜索方法,并且只能用于文件,具体的使用,后面会谈。