前言
Google了一下全网Java的 I/O流文章,发现大部分都写得比较复杂。I/O流涉及到的API比较多,看看下面这张图你就知道了(图片来源于网络):

但是想到我平常只是用到某一小部分(无奈级别还是不够),这里我就做一个简单的归纳,把自己平常用到的做一下记录。还是老样子,我大概画了一下我要讲的I/O框架:

绝大部分的文章都是把字节转换流放到字符流里面讲的,但在这里我为了便于总体思路的梳理,还是决定把字节转换流单独拿出来说一下。
数据是以流的方式进行传输的,那么什么是流呢?不妨想象一下,自来水公司的水通过管道流到你家里的过程。这个过程可以分为以下三个部分进行:
- 公司把蓄水池里的水通过某种装置送到水管里
- 水在管道流动
- 水通过某种装置又回到你家里的蓄水池里
Java的I/O流其实设计到的范围很大,比如标准的输入输出流、文件的操作、对象流以及网络的数据流。按照传输的形式可以分为字节流和字符流,不过在讲这两个之前,我想让大家先明白这两个概念:
-
输入流:程序从输入流读取数据源。数据源包括外界(键盘、文件、网络…),即是将数据源读入到程序的通信通道
-
输出流:程序向输出流写入数据。将程序中的数据输出到外界(显示器、打印机、文件、网络…)的通信通道。
可以看下面的图来帮助理解,数据“Yangc”从A流到了B。对于A来说,“Yangc”就是输出流,而对于B来说就是输入流。

字节流
先说一说字节流,如果在理解字节流的基础上再去看字符流,想必也是一种绝佳的体验。字节流按照流向可以分两类:
- InputStream(字节输入流)
- OutputStream(字节输出流)
这里就用文件操作来写一个例子,我想网络上大都是用文件操作来举例的。先来看看FileOutputStream的用法:
/**
* @author Yangc
* @Time 2020-5-20
* @Action 测试文件输入输出流
* */
public class Test {
public static void main(String[] args) {
String str = "Yangc"; //要写入文件的字符串
File file = new File("src/AAA/test.txt"); //定义文件的名字为file
try {
//如果文件不存在就创建文件
if (!file.exists()){
file.createNewFile();
}
//定义一个字节数组,getBytes()把字符串转化为字节数组
byte[] b = str.getBytes();
//把文件的名字作为参数传递给文件输出流
FileOutputStream outputStream = new FileOutputStream(file);
outputStream.write(b); //把字节数组写入到文件流中
outputStream.close(); //关闭输出流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
这段代码的注释已经写得非常清楚了,我在梳理以下流程:

在这里我们把字符串str写入了文件,可能你还会疑问为什么是FileOutputStream(文件输出)而不是FileInputStream(文件输入)。简单来说,这里的输入输出是相对于我们程序来说的,我们这个程序输出了一个字符串到文件中,所以我们称之为文件输出流。
好了,上面的问题想必你一定非常清楚了。再来看看FileInputStream,大致用法和FileOutputStream差不多:
/**
* @author Yangc
* @Time 2020-5-20
* @Action 测试文件输入输出流
*/
public class Test {
public static void main(String[] args) {
try {
File file = new File("src/AAA/test.txt"); //定义文件的名字
FileInputStream inputStream = new FileInputStream(file); //文件输入流
//定一个int类型的变量,当输入流读到读到最后一个字节的时候会返回-1,则循环结束
int len;
while ((len = inputStream.read()) != -1) {
System.out.print((char)len); //输出:Yangc
}
inputStream.close(); //关闭输入流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
字符流
在理解字节流的基础上,我想字符流也是相当简单的。
文件输出流(FileWriter):
/**
* @author Yangc
* @Time 2020-5-20
* @Action 测试文件输入输出流
*/
public class Test {
public static void main(String[] args) {
File file = new File("src/AAA/test.txt"); //定义文件的名字
String string = "Hello"; //定义要存入的字符串
try {
//FileWriter为输出流,第一个参数为文件名,第二个参数为true,表示把这个字符串加到文件的末尾
FileWriter writer = new FileWriter(file,true);
writer.write(string); //把字符串写入文件中
writer.close(); //关闭文件流
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件输入流(FileReader):
/**
* @author Yangc
* @Time 2020-5-20
* @Action 测试文件输入输出流
*/
public class Test {
public static void main(String[] args) {
File file = new File("src/AAA/test.txt"); //定义文件的名字
try {
FileReader reader = new FileReader(file);
//定一个int类型的变量,当输入流读到读到最后一个字节的时候会返回-1,则循环结束
int len;
while ((len = reader.read()) != -1){
System.out.print((char) len); //强制类型转换
}
reader.close(); //关闭输入流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节转换流
字节转换流分为两种,一种是把字节输入流转(InputStream)换化字符输入流(Reader),另一种是把字节输出流(OutputStram)转化为字符输出流(Writer),看完下面一张图你就明白了。

在这里,我们分别使用了InputStreamReader和OutputStreamWriter进行转换。再来写一个小例子:
public class Test {
public static void main(String[] args) {
String str = "Yangc"; //要写入文件的字符串
File file = new File("src/AAA/test.txt"); //定义文件的名字为file
try {
//如果文件不存在就创建文件
if (!file.exists()){
file.createNewFile();
}
//把文件的名字作为参数传递给文件输出流
FileInputStream inputStream = new FileInputStream(file);
//首先要定义一个缓冲流,它的参数是InputStreamReader类型,而InputStreamReader的参数是inputStream类型
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String s; //定义一个字符串,用来存储缓冲读取的字符
while ((s = bufferedReader.readLine()) != null){
System.out.println(s);
}
inputStream.close(); //关闭输出流
bufferedReader.close(); //关闭缓冲流
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
在这里我使用了字符缓冲流,那么什么是缓冲流呢?为什么要引入缓冲流?
顾名思义,缓冲就是把某种东西保存下来,以便再次使用。我们知道,进行数据访问操作的时候,我们的计算机不断的访问外部设备,会造成传输下降,为了提高效率,才会使用缓冲流。