IO 里面常见类,字节流、字符流、接口、实现类、方法阻塞
IO 流的框架体系
IO 流的概念
Java 的 IO 流是实现输入/输出的基础,它可以方便地实现数据地输入/输出操作,在 Java 中把不同的输入/输出源抽象表述为“流”。流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等
IO 流的分类
输入流和输出流
根据数据流向不同分为:输入流和输出流
输入流:只能从中读取数据,而不能向其写入数据。
输出流:只能向其写入数据,而不能从中读取数据。
字节流和字符流
字节流和字符流的用法几乎完全一样,区别在于字节流和字符流所操作的数单元不同。
字符流的由来是因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。
字节流和字符流的区别
- 读写单位不同,字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
- 处理独享不同,字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符型的数据。
只要处理纯文本数据,就优先考虑使用字符流,除此之外都使用字节流。
节点流和处理流
按照流的角色来分,可以分为节点流和处理流。
可以从/向一个特定的 IO 设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被称为低级流。
处理流时对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能,处理六也被称为高级流。
// 节点流,直接传入的参数是 IO 设备
FileInputStream fis = new FileInputStream("test.txt");
// 处理流,直接传入的参数是流对象
BufferedInputStream bis = new BufferedInputStream(fis);
当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入/输出节点连接。使用处理流的一个明显好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际所访问的数据源也相应地发生变化。实际上,Java 使用处理流来包装节点流是一种典型地装饰器设计模式,通过使用处理流来包装不同地节点流,既可以消除不同节点流地实现差异,也可以提供更方便地方法来完成输入/输出功能。
IO 流的四大基类
根据流的流向以及擦欧总的数据单元不同,将流分为了四种类型,每种类型对应一种抽象基类。这四种抽象基类分别为:InputStream,Reader,OutputStream和Writer。四种基类下,对应不同的实现类,具有不同的特性。在这些实现类中,又可以分为节点流和处理流。
InputStream
InputStream 是所有的输入字节流的父类,它是一个抽象类,主要包含三个方法
// 读取一个字节并以整数的形式返回(0~255),如果返回 -1 已到输入流的末尾
int read();
// 读取一系列字节并存储到一个数组 buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回 -1
int read(byte[] buffer);
// 读取 length 个字节并存储到一个字节数组 buffer,从 off 位置开始村,最多 len,返回实际读取的字节数,如果读取前已到输入流的末尾返回 -1
int read(byte[] buffer,int off,int len);
Reader
Reader 是所有的输入字符流的父类,它是一个抽象类,主要包含三个方法
// 读取一个字符并以整数的形式返回(0~255),如果返回 -1 已到输入流的末尾
int read();
// 读取一系列字符并存储到一个数组 buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回 -1
int read(char[] buffer);
// 读取 length 个字符并存储到一个字节数组 buffer,从 off 位置开始村,最多 len,返回实际读取的字符数,如果读取前已到输入流的末尾返回 -1
int read(char[] buffer,int off,int len);
对比 InputStream 和 Reader 所提供的方法,就不难发现两个基类的功能基本一样的,只不过读取的数据单元不同。
在执行完流操作后,要调用 close() 方法来关闭输入流,因为程序里打开 IO 资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件 IO 资源
除此之外,InputStream 和 Reader 还支持如下方法来移动流中的指针位置
// 在此输入流中标记当前的位置
// readLimit - 在标记位置失效前可以读取字节的最大限制
void mark(int readlimit);
// 测试此输入流是否支持 mark 方法
boolean markSupported();
// 跳过和丢弃此输入流中数据的 n 个字节/字符
long skip(long n);
// 将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
void reset();
OutputStream
OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含四个方法
// 向输出流中写入一个字节数据,该细节数据参数 b 的 低 8 位
void write(int b);
// 将一个字节类型的数组中的数据写入输出流
void write(byte[] b);
// 将一个字节类型的数组中的从指定位置(off)开始的,len 个字节写入到输出流
void write(byte[] b,int off,int len);
// 将输出流中缓冲的数据全部写出到目的地
void flush();
Writer
Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含六个方法
// 向输出流中写入一个字符数据,该字节数据惨此参数 b 的低 16 位
void write(int c);
// 将一个字符类型的数组中的数据写入输出流
void write(char[] cbuf);
// 将一个字符类型的数组中从指定位置(offset)开始的,length 个字符写入到输出流
void write(char[] cbuf,int offset,int length);
// 将一个字符串中的字符写入到输出流
void write(String string);
// 将一个字符串从 offset 开始的 length 个字符写入到输出流
void write(String string,int offset,int length);
// 将输出流中缓冲的数据全部写出到目的地
void flush();
可以看出,Writer 比 OutputStream 多出两个方法,主要是支持写入字符和字符串类性的数据。
使用 Java 的 IO 流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据 flush 到物理节点里(因为在执行 cloase()方法之前,自动执行输出流的flush()方法)