如需转载,请根据 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 许可,附上本文作者及链接。
本文作者: 执笔成念
作者昵称: zbcn
本文链接: https://1363653611.github.io/zbcn.github.io/2020/10/02/IO_01%E6%9E%B6%E6%9E%84/
IO 概述
- 网络操作相关的类在
java.net
包下面。 - 磁盘操作:文件,文件操作
- “流”:有能力产生数据源或者有能力接受数据源的对象(一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。)
- 流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
- 流有输入和输出,输入时是流从数据源流向程序。输出时是流从程序传向数据源,而数据源可以是内存,文件,网络或程序等。
按照操作方式分类结构图
按照操作对象分类
常用流关系图
输入流和输出流
- 依据数据的流向不同,分为输出流和输入流
- 输入流(只读不写):只能从中读取数据,而不能向其写入数据。
- 输出流(只写不读):只能向其写入数据,而不能从中读取数据。
字节流和字符流
- 区别: 字节流和字符流所操作的数据单元不同。
- 字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。
- 字符流的本质:是基于字节流读取时,去查了指定的码表。
字节流和字符流的区别:
- 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
- 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
节点流和处理流
- 按照流的角色来分,可以分为节点流和处理流。
- 可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被成为低级流
- 处理流(缓冲流)是对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能,处理流也被称为高级流。
1 | //节点流直接传入的是IO 设备 |
2 | FileInputStream fis = new FileInputStream("test.txt"); |
3 | //处理流,直接传入的参数是流对象 |
4 | BufferedInputStream bis = new BufferedInputStream(fis); |
- 当使用处理流进行输入/输出时,程序并不会直接连接到实际的数据源,没有和实际的输入/输出节点连接。
- 使用 处理流 好处是,只要使用相同的处理流,程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际所访问的数据源也相应地发生变化。
- Java使用处理流来包装节点流是一种典型的装饰器设计模式,通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入/输出功能。
IO的四大基类
- 根据流的流向以及操作的数据单元不同,将流分为了四种类型,每种类型对应一种抽象基类。
- 输入两种:
InputStream
、Reader
- 输出两种:
OutputStream
、Writer
InputStream,Reader,OutputStream以及Writer,这四大抽象基类,本身并不能创建实例来执行输入/输出,但它们将成为所有输入/输出流的模版,所以它们的方法是所有输入/输出流都可以使用的方法。类似于集合中的Collection接口。
InputStream
InputStream
是所有的输入字节流的父类,它是一个抽象类,主要包含三个方法:
1 | //读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 |
2 | int read() ; |
3 | //读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。 |
4 | int read(byte[] buffer) ; |
5 | //读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。 |
6 | int read(byte[] buffer, int off, int len) ; |
Reader
Reader 是所有的输入字符流的父类,它是一个抽象类,主要包含三个方法:
1 | //读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。 |
2 | int read() ; |
3 | //读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。 |
4 | int read(char[] cbuf) ; |
5 | //读取length个字符,并存储到一个数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。 |
6 | int read(char[] cbuf, int off, int len) |
总结
- 对比
InputStream
和Reader所提供的方法,就不难发现两个基类的功能基本一样的,只不过读取的数据单元不同。 - 在执行完流操作后,要调用
close()
方法来关系输入流,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。
InputStream和Reader还支持如下方法来移动流中的指针位置
1 | //在此输入流中标记当前的位置 |
2 | //readlimit - 在标记位置失效前可以读取字节的最大限制。 |
3 | void mark(int readlimit) |
4 | // 测试此输入流是否支持 mark 方法 |
5 | boolean markSupported() |
6 | // 跳过和丢弃此输入流中数据的 n 个字节/字符 |
7 | long skip(long n) |
8 | //将此流重新定位到最后一次对此输入流调用 mark 方法时的位置 |
9 | void reset() |
OutputStream
OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下四个方法
1 | //向输出流中写入一个字节数据,该字节数据为参数b的低8位,高24位被忽略。 |
2 | void write(int b) ; |
3 | //将一个字节类型的数组中的数据写入输出流。 |
4 | void write(byte[] b); |
5 | //将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。 |
6 | void write(byte[] b, int off, int len); |
7 | //将输出流中缓冲的数据全部写出到目的地。 |
8 | void flush(); |
Writer
Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含如下六个方法:
1 | //向输出流中写入一个字符数据,该字节数据为参数b的低16位。 |
2 | void write(int c); |
3 | //将一个字符类型的数组中的数据写入输出流, |
4 | void write(char[] cbuf) |
5 | //将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。 |
6 | void write(char[] cbuf, int offset, int length); |
7 | //将一个字符串中的字符写入到输出流。 |
8 | void write(String string); |
9 | //将一个字符串从offset开始的length个字符写入到输出流。 |
10 | void write(String string, int offset, int length); |
11 | //将输出流中缓冲的数据全部写出到目的地。 |
12 | void flush() |
输出总结
- Writer比
OutputStream
多出两个方法,主要是支持写入字符和字符串类型的数据。 - 使用Java的IO流执行输出时,必须关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)