IO 是指 Input/Output,即输入和输出。以内存为中心:

  • Input 指从外部读入数据到内存。例如:把文件从磁盘读取到内存,从网络中读取数据到内存
  • Output 指把数据从内存输出到外部。例如:把数据写入到磁盘中,把数据从内存输出到网络中

IO 流是一种顺序读写数据的模式,它的特点是单向流动。数据类似自来水一样在水管中流动,所以我们把它称为 IO 流。按照输入和输出,IO 流分为输入、输出两种 IO 流,每种输入、输出流又分为字节流和字符流两大类。

  • 字节流以字节 byte 为单位处理输入、输出操作
  • 字符流以字符 char 为单位处理输入、输出操作

    InputStream/OutputStream 字节流

    从磁盘读入一个文件,包含 6 个字节,就相当于读入了 6 个字节的数据,读取过程是按顺序进行的称为输入字节流
    1. ╔════════════╗
    2. Memory
    3. ╚════════════╝
    4. 0x48
    5. 0x65
    6. 0x6c
    7. 0x6c
    8. 0x6f
    9. 0x21
    10. ╔═══════════╗
    11. Hard Disk
    12. ╚═══════════╝
    反过来,把 6 个字节从内存写入磁盘文件中则称为输出字节流
    1. ╔════════════╗
    2. Memory
    3. ╚════════════╝
    4. 0x21
    5. 0x6f
    6. 0x6c
    7. 0x6c
    8. 0x65
    9. 0x48
    10. ╔═══════════╗
    11. Hard Disk
    12. ╚═══════════╝

    Java 中,InputStream 是所有输入字节流的基类,OutputStream 是所有输出字节流的基类

Reader/Writer 字符流

char[] 数组 Hi你好 这 4 个字符用 Writer 字符流写入文件,并且使用 UTF-8 编码,得到的最终文件是 8 个字节,英文字符 Hi 各占一个字节,中文 你好 各占 3 个字节:

  1. 0x48
  2. 0x69
  3. 0xe4bda0
  4. 0xe5a5bd

反过来,用 Reader 读取以 UTF-8 编码的这 8 个字节,得到 Hi你好 这 4 个字符。
上述整个过程,ReaderWriter 本质上是一个自动编解码的 InputStreamOutputStreamReader 内部把读入的数据从 byte 转换为了 char

字节流和字符流的用法几乎完全一样,如何选择使用 Reader 和 InputStream 要取决于具体的使用场景。如果数据源不是文本使用 InputStream,反之则使用 Reader

java.io 包

java.io 包中涉及处理 IO 流的有 40+ 类,这些类看起来很杂乱,但实际上很有规则,而且彼此之间存在非常紧密的联系,这 40+ 类都是从 InputStream/OutputStream、Reader/Writer 4个抽象基类中派生出来的
按操作方式分类结构图:
IO - 图1
按操作对象分类结构图:
IO - 图2

BIO、NIO 和 AIO 之间的区别

BIO

BIOBlocking I/O 全称同步阻塞 I/O 模型。此模型下数据的读取和写入必须阻塞在一个线程内等待其完成。这种适用于活动连接数不是特别高(小于单机 1000)的应用场景,可以让每一个连接专注于自己的 I/O 并且编码相对简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的

NIO

NIONon-blocking/New I/O 全称同步非阻塞 I/O 模型。非阻塞模型的应用场景与阻塞模型应用场景相反,它适用于高负载、高并发的(网络)应用。Java 1.4 引入了 NIO 框架,对应 java.nio 包,框架提供了 ChannelSelectorBuffer 等抽象,它支持面向缓冲的,基于通道的 I/O 操作方法

AIO

AIOAsynchronous I/O 全称异步非阻塞 I/O 模型。AIOBIONIO 完全不同,它是一种全新的设计。AIO 是基于事件和回调机制实现的,程序执行代码之后会直接返回,不会造成阻塞,当后台处理完成后,操作系统在通知相应的线程进行后续的操作。Java 7 中引入了 AIO