InputStream、InputStreamReader 和 BufferedReader 如何在 Java 中协同工作?

How do an InputStream, InputStreamReader and BufferedReader work together in Java?

我正在学习 Android 开发(我是一般编程的初学者)并学习 HTTP 网络,并在课程中看到了这段代码:

private String readFromStream(InputStream inputStream) throws IOException {
  StringBuilder output = new StringBuilder();
  if (inputStream != null) {
    InputStreamReader inputStreamReader = new InputStreamReader(inputStream, Charset.forName("UTF-8"));
    BufferedReader reader = new BufferedReader(inputStreamReader);
    String line = reader.readLine();
    while (line != null) {
      output.append(line);
      line = reader.readLine();
    }
  }
  return output.toString();
}

我不明白 InputStream、InputStreamReader 和 BufferedReader 到底是做什么的。它们都有一个 read() 方法,在 BufferedReader.Why 的情况下还有 readLine() ,我不能只使用 InputStream 或只添加 InputStreamReader 吗?为什么我需要添加 BufferedReader?我知道这与效率有关,但我不明白。

我一直在研究,documentation for the BufferedReader 试图解释这一点,但我仍然不明白谁在做什么:

In general, each read request made of a Reader causes a corresponding read request to be made of the underlying character or byte stream. It is therefore advisable to wrap a BufferedReader around any Reader whose read() operations may be costly, such as FileReaders and InputStreamReaders. For example,

BufferedReader in = new BufferedReader(new FileReader("foo.in")); will buffer the input from the specified file. Without buffering, each invocation of read() or readLine() could cause bytes to be read from the file, converted into characters, and then returned, which can be very inefficient.

所以,我知道 InputStream 只能读取一个字节,InputStreamReader 只能读取一个字符,而 BufferedReader 只能读取整行,而且它还可以提高效率,这是我不明白的。我想更好地了解谁在做什么,以便理解为什么我需要所有这三个以及没有其中一个会有什么区别。

我在这里和网络上的其他地方进行了很多研究,似乎没有找到任何我能理解的解释,几乎所有教程都只是重复文档信息。这里有一些相关的问题,也许可以开始解释这一点,但不要更深入地解决我的困惑:Q1, Q2, Q3, 。我想这可能与最后一个问题对系统调用和返回的解释有关。但我想了解这一切的含义。

难道是BufferedReader的readLine()调用了InputStreamReader的read()方法,InputStreamReader的read()方法又调用了InputStream的read()方法?并且 InputStream returns 字节转换为 int,一次返回一个字节,InputStreamReader 读取足够多的字节来生成一个字符并将其转换为 int 并且一次 returns 一个字符,并且 BufferedReader 读取足够多的这些表示为整数的字符来组成整行?并且 returns 整行作为一个字符串,只返回一次而不是多次?我不知道,我只是想知道事情是如何运作的。

非常感谢!

这个Streams in Java concepts and usagelink,给个很好的解释。

Streams、Readers、Writers、BufferedReader、BufferedWriter – 这些是您将在 Java 中处理的术语。在Java中提供了类来操作输入和输出。真正值得了解它们之间的关系以及它们的使用方式。 post 将详细探讨 Java 和其他相关 类 中的流。那么让我们开始吧:

让我们在高层次上定义每一个,然后深入挖掘。


用于处理字节级数据

Reader/Writer
用来处理人物等级。它还支持各种字符编码。

BufferedReader/BufferedWriter
以提高性能。需要读取的数据会缓存到内存中,以便快速访问。

虽然这些用于获取输入,但也只有相应的 类 用于输出。例如,如果有一个用于读取字节流的 InputStream,而 OutputStream 将有助于写入字节流。

输入流
java 提供了多种类型的 InputStreams。每个都连接到不同的数据源,例如字节数组、文件等。

例如,FileInputStream 连接到文件数据源,可用于从文件中读取字节。而 ByteArrayInputStream 可用于将字节数组视为输入流。

OutputStream
这有助于将字节写入数据源。对于几乎每个 InputStream 都有一个相应的 OutputStream,只要它有意义。


更新

什么是缓冲流?

这里我引用了Buffered Streams,Java文档(有技术解释):

Buffered Streams

Most of the examples we've seen so far use unbuffered I/O. This means each read or write request is handled directly by the underlying OS. This can make a program much less efficient, since each such request often triggers disk access, network activity, or some other operation that is relatively expensive.

To reduce this kind of overhead, the Java platform implements buffered I/O streams. Buffered input streams read data from a memory area known as a buffer; the native input API is called only when the buffer is empty. Similarly, buffered output streams write data to a buffer, and the native output API is called only when the buffer is full.

有时我会在阅读技术文档时头晕目眩。所以,这里我引用更人性化的解释来自https://yfain.github.io/Java4Kids/:

In general, disk access is much slower than the processing performed in memory; that’s why it’s not a good idea to access the disk a thousand times to read a file of 1,000 bytes. To minimize the number of times the disk is accessed, Java provides buffers, which serve as reservoirs of data.

In reading File with FileInputStream then BufferedInputStream, the class BufferedInputStream works as a middleman between FileInputStream and the file itself. It reads a big chunk of bytes from a file into memory (a buffer) in one shot, and the FileInputStream object then reads single bytes from there, which are fast memory-to-memory operations. BufferedOutputStream works similarly with the class FileOutputStream.

The main idea here is to minimize disk access. Buffered streams are not changing the type of the original streams — they just make reading more efficient. A program performs stream chaining (or stream piping) to connect streams, just as pipes are connected in plumbing.

  • InputStream, OutputStream, byte[], ByteBuffer 用于 binary 数据。
  • Reader, Writer, String, char 用于 text,内部 Unicode,因此世界上所有的文字都可以组合(比如希腊语和阿拉伯语)。

  • InputStreamReaderOutputStreamWriter形成了两者之间的桥梁。如果您有一些 InputStream 并且知道它的字节实际上是某种编码字符集的文本,那么您可以包装 InputStream:

    try (InputStreamReader reader =
            new InputStreamReader(stream, StandardCharsets.UTF_8)) {
         ... read text ...
    }
    

有一个没有 Charset 的构造函数,但它不可移植,因为它使用默认的平台编码。

On Android StandardCharset 可能不存在,请使用 "UTF-8"。

派生的 classes FileInputStreamBufferedReader 分别向父 InputStream 添加一些内容。 Reader.

A FileInputStream 用于来自 File 的输入,而 BufferedReader 使用内存缓冲区,因此实际物理读取不会读取字符(效率低下)。使用 new BufferedReader(otherReader) 可以为原始 reader.

添加缓冲

所有这些都明白,有实用程序 class FilesnewBufferedReader(Path, Charset) 这样的方法,增加了额外的简洁性。

我已经阅读了很多关于这个主题的文章。我希望这能以某种方式帮助你。

基本上,BufferedReader 维护一个内部缓冲区

在读取操作期间,它从 bulk 中的文件中读取字节并将这些字节存储在其内部缓冲区中。

现在,对于每个读取操作,字节都从该内部缓冲区传递给程序。

这减少了程序与文件或磁盘之间的通信次数。因此效率更高。