我正在研究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只能读取整行,并且它还可以解决效率问题,这是我无法获得的。我想更好地了解谁在做什么,以便理解为什么我需要他们三个以及没有他们之一的差别。
我已经在网上和网上的其他地方进行了大量研究,似乎没有找到任何我能理解的解释,几乎所有的教程都只重复文档信息。以下是一些相关的问题,可能会开始解释这一点,但不要深入解决我的困惑:Q1,Q2,Q3,Q4。我认为这可能与最后一个问题有关系统调用和返回的解释有关。但我想了解这一切的含义。
可能是BufferedReader的readLine()调用了InputStreamReader的read()方法,而该方法又调用了InputStream的read()方法吗?并且InputStream返回转换为int的字节,一次返回一个字节,InputStreamReader读取足够的这些字符以生成单个字符并将其转换为int并一次返回一个字符,并且BufferedReader读取足够的这些字符用整数表示整数?并将整行作为String返回,只返回一次而不是几次?我不知道,我只是想弄清楚事情的运作方式。
提前多多谢意!
答案 0 :(得分:13)
这个Streams in Java concepts and usage链接,给出了非常好的解释。
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提供缓冲区,用作 储存数据。
在使用FileInputStream读取File然后使用BufferedInputStream class BufferedInputStream充当FileInputStream之间的中间人 和文件本身。它从文件中读取一大块字节 一次性内存(缓冲区),然后是FileInputStream对象 从那里读取单个字节,这是快速的内存到内存 操作。 BufferedOutputStream与类的工作方式类似 FileOutputStream中。
这里的主要思想是最小化磁盘访问。缓冲流是 不改变原始流的类型 - 他们只是阅读 更高效。程序执行流链接(或流管道) 连接流,就像管道连接管道一样。
答案 1 :(得分:1)
InputStream, OutputStream, byte[], ByteBuffer
适用于二进制数据。 Reader, Writer, String, char
适用于文本,内部为Unicode,因此世界上所有脚本都可以合并(例如希腊语和阿拉伯语)。
InputStreamReader
和OutputStreamWriter
构成两者之间的桥梁。如果你有一些InputStream,并且知道它的字节实际上是某些编码中的文本,Charset,那么你可以包装InputStream:
try (InputStreamReader reader =
new InputStreamReader(stream, StandardCharsets.UTF_8)) {
... read text ...
}
有一个没有Charset的构造函数,但这不是可移植的,因为它使用默认的平台编码。
在Android StandardCharset上可能不存在,请使用“UTF-8”。
派生类FileInputStream
和BufferedReader
向父InputStream
添加内容。 Reader
。
FileInputStream用于来自 File 的输入,而BufferedReader使用内存缓冲区,因此实际的物理读取不会以字符方式读取(效率低下)。使用new BufferedReader(otherReader)
,您可以为原始阅读器添加缓冲。
所有这一切都明白了,实用程序类Files
使用newBufferedReader(Path, Charset)
这样的方法,这些方法可以增加额外的简洁性。
答案 2 :(得分:0)
我已经阅读了很多有关该主题的文章。希望对您有所帮助。
基本上,BufferedReader维护一个内部缓冲区。
在读取操作期间,它将从批量中的文件读取字节,并将该字节存储在其内部缓冲区中。
现在,每次读取操作都会从该内部缓冲区将字节传递给程序。
这减少了程序与文件或磁盘之间的通信次数。因此效率更高。