IO介绍
# 00.IO介绍
简单介绍下IO的概念和Java中IO的体系。
# 什么是IO
在冯诺依曼结构中,IO是重要的组成部分,几乎所有编程语言都涉及IO。IO是指Input/Output,即输入和输出,这里的输入和输出都是以内存(存储器)为中心的:
- CPU只处理内存中的数据,因此如果要处理不在内存中的数据,要先将数据输入(Input)到内存,例如将文件加载到内存,或者读取网络上的数据(例如数据库)到内存。
- 内存是易失性的,关机或者宕机后,数据就没有了。所以如果要保存内存中的数据,就得将数据输出(Output)到文件里,或者输出到网络接口(例如数据库)。
我们的Java程序也是在内存中由CPU运行的,内存中的数据无非就是0和1,这里的0和1可能是字符数据,也可能是二进制数据,可以用Java的byte[]
,String
来表示并处理。
# 什么是流
在程序中,我们经常需要读取文件,那么如何读取比较好呢?比如一个大文件,不可能瞬间就能读取到内存里,读取速度取决于硬件的性能;并且内存里也不一定有足够的内存放下整个文件。
因此,一个文件的数据,我们都是一部分一部分地读取的。这种读取方式,我们称为流,数据就像水流一样一点一点地流入到内存里;输出也是一样的,向水流一样流出到硬盘上,因此我们也可以称这种读取方式为 IO 流,IO流是一种顺序读写数据的模式。
字节是计算机存储单元的基本单位,因此IO流也可以称为字节流,例如我们读取一个6字节大小的文件,相当于读入了6个字节的数据:
╔════════════╗
║ Memory ║
╚════════════╝
▲
│0x48
│0x65
│0x6c
│0x6c
│0x6f
│0x21
╔═══════════╗
║ Hard Disk ║
╚═══════════╝
2
3
4
5
6
7
8
9
10
11
12
13
我们按顺序读取这6个字节,这种读取方式是输入字节流;
反过来,将6个字节存储到磁盘上,就是输出字节流
╔════════════╗
║ Memory ║
╚════════════╝
│0x21
│0x6f
│0x6c
│0x6c
│0x65
│0x48
▼
╔═══════════╗
║ Hard Disk ║
╚═══════════╝
2
3
4
5
6
7
8
9
10
11
12
13
# Java中的IO流
在Java中,有许多用于各种目的的IO类(例如读取文件的IO类,读取网络接口的IO类),我们可以简单地将它们分为输入类和输出类:
InputStream
,代表输入字节流。是一个抽象类,所有输入流的超类。OuputStream
,代表输出字节流。也是抽象类,是所有输出流的超类。
图片来自《Java语言程序设计-基础篇》17.1节
除了字节之外,我们可能还会读写字符,因此Java提供了Reader
和Writer
,用来读写字符;但是,计算机中只存储0和1,Reader
和Writer
本质上也是读取字节流的;只不过他们能在读取字节的时候自动解码为char字符,输出字节的时候自动编码为二进制数,所以看起来就是直接读写字符了:
图片来自《Java语言程序设计-基础篇》17.2节
我们也可以使用InputStream
读取字符为byte[]
数组,然后根据编码转换为字符串。
一般情况下,使用二进制文件的效率更高:
- 例如存储一个整数199,只需存储199的二进制数字C7即可;而如果用文本文件,则需存储‘1’,‘9’,‘9’三个字符,需要存储的空间更大
- 二进制IO不需要编码和解码,比文本IO类效率高
- 二进制文件与主机的编码方案无关,因此是可移植的,在任何机器上的Java程序都可以读取Java程序所创建的二进制文件,这也是为什么编译后的字节码文件是二进制的原因。
# 同步和异步
同步IO是指,读写IO时代码必须等待数据返回后才继续执行后续代码,它的优点是代码编写简单,缺点是CPU执行效率低。
而异步IO是指,读写IO时仅发出请求,然后立刻执行后续代码,它的优点是CPU执行效率高,缺点是代码编写复杂。
Java标准库的包java.io
提供了所有同步IO的功能,而java.nio
则提供了异步IO。我们只讨论Java的同步IO,对于异步IO感兴趣的同学可以去看看Netty
上面我们讨论的InputStream
、OutputStream
、Reader
和Writer
都是同步IO的抽象类,要使用IO都得使用对应的具体实现类,以读写文件为例,有FileInputStream
、FileOutputStream
、FileReader
和FileWriter
。
# 小结
什么是流:流是一种流式的数据输入/输出模型。
在Java中,java.io
包提供了同步IO功能。主要有两种流
- 字节流接口:
InputStream
/OutputStream
,所有输入/输出流的父类,二进制数据以byte
为最小单位流动; - 字符流接口:
Reader
/Writer
,字符数据以char
为最小单位流动
Java.nio则提供了异步IO。