Java扫盲篇,乞丐版的I/O流

446 阅读5分钟

前言


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),看完下面一张图你就明白了。

在这里,我们分别使用了InputStreamReaderOutputStreamWriter进行转换。再来写一个小例子:

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();
        }

    }
}

在这里我使用了字符缓冲流,那么什么是缓冲流呢?为什么要引入缓冲流?

顾名思义,缓冲就是把某种东西保存下来,以便再次使用。我们知道,进行数据访问操作的时候,我们的计算机不断的访问外部设备,会造成传输下降,为了提高效率,才会使用缓冲流。

参考资料:java回忆录—输入输出流详细讲解(入门经典)