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

时间:2017-03-31 18:07:25

标签: java android inputstream bufferedreader inputstreamreader

我正在研究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的作用。在BufferedReader的情况下,它们都有一个read()方法和readLine()。为什么我只能使用InputStream或只添加InputStreamReader?为什么我需要添加BufferedReader?我知道这与效率有关,但我不明白如何。

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

  

通常,每个读取请求都会产生相应的读取请求   读取基础字符或字节流的请求。它   因此建议在任何Reader周围包装一个BufferedReader   其read()操作可能代价高昂,例如FileReaders和   InputStreamReaders。例如,

     

BufferedReader in = new BufferedReader(new FileReader("foo.in"));   将缓冲指定文件的输入。没有缓冲,每个   调用read()或readLine()可能会导致读取字节   该文件,转换为字符,然后返回,这可以是   非常低效。

所以,据我所知,InputStream只能读取一个字节,InputStreamReader只能读取一个字符,而BufferedReader只能读取整行,并且它还可以解决效率问题,这是我无法获得的。我想更好地了解谁在做什么,以便理解为什么我需要他们三个以及没有他们之一的差别。

我已经在网上和网上的其他地方进行了大量研究,似乎没有找到任何我能理解的解释,几乎所有的教程都只重复文档信息。以下是一些相关的问题,可能会开始解释这一点,但不要深入解决我的困惑:Q1Q2Q3Q4。我认为这可能与最后一个问题有关系统调用和返回的解释有关。但我想了解这一切的含义。

可能是BufferedReader的readLine()调用了InputStreamReader的read()方法,而该方法又调用了InputStream的read()方法吗?并且InputStream返回转换为int的字节,一次返回一个字节,InputStreamReader读取足够的这些字符以生成单个字符并将其转换为int并一次返回一个字符,并且BufferedReader读取足够的这些字符用整数表示整数?并将整行作为String返回,只返回一次而不是几次?我不知道,我只是想弄清楚事情的运作方式。

提前多多谢意!

3 个答案:

答案 0 :(得分:13)

这个Streams in Java concepts and usage链接,给出了非常好的解释。

This

Streams,Readers,Writers,BufferedReader,BufferedWriter - 这些是您将在Java中处理的术语。 Java中提供了用于输入和输出的类。值得了解这些是如何相关以及如何使用它们。本文将详细探讨Java中的Streams和其他相关类。那么让我们开始吧:

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

<强>流
用于处理字节级数据

<强>读/写
用于处理角色等级。它还支持各种字符编码。

<强>的BufferedReader /的BufferedWriter
提高性能。要读取的数据将缓冲到内存中以便快速访问。

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

<强> InputStreams
java提供了许多类型的InputStreams。每个都连接到不同的数据源,如字节数组,文件等。

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

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

<强>更新

什么是缓冲流?

我在这里引用Buffered Streams,Java文档(有技术说明):

  

缓冲流

     

到目前为止,我们看到的大多数示例都使用无缓冲的I / O.这意味着   每个读或写请求由底层OS直接处理。   由于每个这样的请求,这可以使程序效率低得多   经常触发磁盘访问,网络活动或其他一些操作   这是相对昂贵的。

     

为了减少这种开销,Java平台实现了缓冲   I / O流。缓冲输入流从已知的存储区读取数据   作为缓冲;仅在缓冲区时才调用本机输入API   空。类似地,缓冲输出流将数据写入缓冲区,并且   仅当缓冲区已满时才会调用本机输出API。

有时我会在阅读技术文档时失去头发。所以,我在这里引用https://yfain.github.io/Java4Kids/更人道的解释

  

通常,磁盘访问比执行的处理慢得多   在记忆中;这就是为什么访问磁盘不是一个好主意   千次读取1000字节的文件。尽量减少数量   在访问磁盘的时候,Java提供缓冲区,用作   储存数据。

     

enter image description here

     

在使用FileInputStream读取File然后使用BufferedInputStream   class BufferedInputStream充当FileInputStream之间的中间人   和文件本身。它从文件中读取一大块字节   一次性内存(缓冲区),然后是FileInputStream对象   从那里读取单个字节,这是快速的内存到内存   操作。 BufferedOutputStream与类的工作方式类似   FileOutputStream中。

     

这里的主要思想是最小化磁盘访问。缓冲流是   不改变原始流的类型 - 他们只是阅读   更高效。程序执行流链接(或流管道)   连接流,就像管道连接管道一样。

答案 1 :(得分:1)

  • InputStream, OutputStream, byte[], ByteBuffer适用于二进制数据。
  • Reader, Writer, String, char适用于文本,内部为Unicode,因此世界上所有脚本都可以合并(例如希腊语和阿拉伯语)。

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

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

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

在Android StandardCharset上可能不存在,请使用“UTF-8”。

派生类FileInputStreamBufferedReader向父InputStream添加内容。 Reader

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

所有这一切都明白了,实用程序类Files使用newBufferedReader(Path, Charset)这样的方法,这些方法可以增加额外的简洁性。

答案 2 :(得分:0)

我已经阅读了很多有关该主题的文章。希望对您有所帮助。

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

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

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

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