- 基本介绍
I/O问题是任何编程语言都无法回避的问题,可以说I/O问题是整个人机交互的核心问题,因为I/O是机器获取和信息交互的主要渠道。在这个数据大爆炸的时,I/O问题很突出,很容易成为一个性能瓶颈。Java也一直在持续优化I/O问题,比如NIO的引进,提升了I/O的性能。
I/O的操作类主要在Java.io包下,大概有80个类,这些类大概分为以下四组。
- 基于字节操作的I/O接口:InputStream与OutputStream
- 基于字符操作的I/O接口:Writer与Reader
- 基于磁盘操作的I/O接口:File
- 基于网络操作的I/O接口:Socket
前两组主要是传输数据的格式,后两组主要是传输数据的方式。虽然Socket类不在Java.io包下,但是归根结底还是传输数据。
2.基于字节的I/O操作接口
基于字节的I/O操作接口输入和输出分别是InputStream和OutputStream,
2.1 InputStream的类层次结构如下图所示
对于使用,我们在这里需要介绍一下装饰模式:
装饰模式:给一个类添加一些额外的职责,并且在添加这些额外的职责时不会控制该类的执行逻辑。 装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。装饰模式以对客户端透明的方式动态的给一个对象附加上更多的责任。换言之客户端并不会觉的对象在装饰前和装饰后有什么区别。实例化 装饰者子类的时候,是调用构建者子类来实例化,不会调用装饰者类,装饰者类对客户端来说是透明的,只是对功能的扩张。
装饰模式的各个角色:
(1)抽象构件(Component)角色:由InputStream扮演。这是一个抽象类,为各种子类型流处理器提供统一的接口。
(2)具体构件(ConcreteComponent)角色:由ByteArrayInputStream、FileInputStream、PipedInputStream以及StringBufferInputStream等原始流处理器扮演。他们实现了抽象构件角色所规定的接口,可以被链接流处理器所装饰。
(3)抽象装饰(Decorator)角色:由FilterInputStream扮演。它实现了InputStream所规定的接口。
(4)具体装饰(ConcreteDecorator)角色:由几个类扮演,分别是DataInputStream、BufferInputStream
举个栗子:
DataInputStream out=new DataIntputStream(new FileInputStream());
这就是 装饰者模式,DataInputStream是装饰者子类,FileInputStream是实现接口的子类。
这里不会调用到装饰者类--FilteroutputStream,只是作为继承的另一种方案,对客户端来说是透明的,是为了功能的扩张.
2.2 OutputStream的类层次结构如下图所示
这个类族的使用方法可以参考装饰者模式。
3.基于字符的的I/O操作接口
不管是磁盘还是网络传输,最小的存储单元都是字节,而不是字符,所以I/O操作的都是字节而不是字符,但是为什么有操作字符的都是I/O接口?这是因为在我们的程序中通常操作的数据都是字符形式,为了操作方便要提供一个直接写字符的接口。
下面所示的是写字符I/O操作接口所涉及的类Writer提供了一个抽象方法write(char cbuf[],int off,int len)。
Reader类提供了一个读抽象方法read(char cbuf[],int off,int len),返回读到的n个字节数。具体如下所示
不管是Reader还是Writer都只定义了读取或者写入的数据字符的方式,但没有定义写在哪里,这些内容就是磁盘和网络的工作机制。
3 字节与字符的转化接口
· 因为数据持久化都是义字符进行的,所以必须有从字符到字节或者从字节到字符的转化。其中读的转化过程如下图所示
InputStreamReader是从字节到字符转化的桥梁,从InputStream到Reader的过程要设定指定字符集,否则将采用操作系统指定字符集,很可能会出现乱码。StreamDecoder正是从字节到字符解码的实现类。若按如下方式读取一个文件。
try{
StringBuffewr sb=new StringBuffer();
char[] buf=new char[1024];
FileReader f=new FileReader("file");
while(f.read(buf)>0){
sb.append(buf);
}
sb.toString
}catch(IOException){
}
FileReader类就是按照上面的方式读取文件的,FileReader继承了InputStreamReader类,实际上是读取文件流,然后通过StreamDecoder解码成char,只不过这里的解码字符集是默认字符ji。
写入也是类似的过程;
只不过StreamEncoder把从字符到字节的编码过程。