1. 概述

FileReaderBufferedReader 是两个可以从输入流中读取字符的类。

在这个教程中,我们将探讨它们之间的差异。

2. FileReader

FileReader 类可以从文件中读取字符流。 此外,它只能逐个字符读取,每次调用其 read() 方法时,都会直接访问硬盘上的文件,从中读取一个字符。因此,FileReader 在读取文件字符时本身非常慢且效率低下。另外,FileReader 只能读取文件中的字符,不能处理其他类型的输入流。

2.1. 构造函数

FileReader 有三个构造函数:

  • FileReader(File file):接收一个 File 对象作为参数
  • FileReader(FileDescriptor fd):接收一个 FileDescriptor 作为参数
  • FileReader(String fileName):接收包含路径的文件名作为参数

2.2. 返回值

每次调用 read() 方法,它都会返回一个整数值,表示从文件中读取到的字符的 Unicode 值,如果到达了字符流的末尾,则返回 -1。

2.3. 示例

让我们通过一个例子来了解如何使用 FileReader 从内容为 "qwerty" 的文本文件中读取字符:

@Test
public void whenReadingAFile_thenReadsCharByChar() {
    StringBuilder result = new StringBuilder();

    try (FileReader fr = new FileReader("src/test/resources/sampleText2.txt")) {
        int i = fr.read();

        while(i != -1) {
            result.append((char)i);

            i = fr.read();
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    assertEquals("qwerty", result.toString());
}

在上述代码中,我们在将返回值转换为 char 类型后,将其附加到结果字符串上。

3. BufferedReader

BufferedReader 类创建一个缓冲区来存储来自字符输入流的数据。此外,输入流可以是文件、控制台、字符串或其他类型的字符流。

它的构造函数接收一个 Reader 作为字符输入流。因此,我们可以给任何实现了 Reader 抽象类的类提供给 BufferedReader 作为输入流,以便从中读取字符。

当我们开始从 BufferedReader 开始读取时,它会从输入流中读取整个数据块并将其存储在缓冲区中。之后,如果我们继续从 BufferedReader 读取,它将从缓冲区而不是底层字符流返回字符,直到缓冲区为空。然后,它会从输入流中再读取一个数据块,并将其存储在缓冲区中,供后续读取调用。

BufferedReader 类减少了对输入流的读取操作,从缓冲区读取通常比访问底层输入流快得多。因此,BufferedReader 提供了一种更快、更有效的方法来读取字符流。

3.1. 构造函数

BufferedReader 有两个构造函数:

  • BufferedReader(Reader in):接收字符输入流(必须实现 Reader 抽象类)作为参数
  • BufferedReader(Reader in, int sz):接收字符输入流和缓冲区大小作为参数

3.2. 返回值

如果调用 read() 方法,它将返回一个整数值,表示从输入流中读取到的字符的 Unicode 值。如果调用 readLine() 方法,它会从缓冲区中读取一行并以字符串值返回。

3.3. 示例

让我们使用 BufferedReader 和一个 InputStreamReader 实现,从一个包含三行内容的文本文件中读取字符:

@Test
public void whenReadingAFile_thenReadsLineByLine() {
    StringBuilder result = new StringBuilder();
    
    final Path filePath = new File("src/test/resources/sampleText1.txt").toPath();
    try (BufferedReader br = new BufferedReader(new InputStreamReader(Files.newInputStream(filePath), StandardCharsets.UTF_8))) {
        String line;

        while((line = br.readLine()) != null) {
            result.append(line);
            result.append('\n');
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    assertEquals("first line\nsecond line\nthird line\n", result.toString());
}

这段测试代码成功运行,说明 BufferedReader 成功从文件中读取了所有三行文本。

4. 区别是什么?

BufferedReaderFileReader 快且更高效,因为它从输入流中一次性读取整个数据块,并将其保留在缓冲区中供后续读取使用,而 FileReader 需要为每个字符访问文件。此外,FileReader 只能逐字符读取,而 BufferedReader 具有如 readLine() 这样的方法,可以从缓冲区中读取整行。最后,FileReader 只能读取文件,而 BufferedReader 可以读取任何类型的字符输入流(如文件、控制台、字符串等):

  • FileReader

  • BufferedReader

较慢且效率较低

更快且更高效

只能逐字符读取

可以读取字符和行

只能读取文件

可以读取任何类型的字符流

对于小文件或文件数据上的读取操作较少的情况,FileReader 可能就足够了。但对于大文件或频繁进行数据读取的操作,BufferedReader 是更好的选择。

5. 总结

在这个教程中,我们学习了如何使用 FileReaderBufferedReader,以及它们之间的差异。

如往常一样,本教程的完整源代码可以在 GitHub 上找到。