Как работают InputStream, InputStreamReader и BufferedReader в 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 (), а также readLine () в случае BufferedReader. Почему я не могу использовать InputStream или добавлять только InputStreamReader? Почему мне нужно добавить BufferedReader? Я знаю, что это связано с эффективностью, но я не понимаю, как это сделать.

Я изучал, и документация для BufferedReader пытается объяснить это, но я до сих пор не понимаю, кто что делает:

В общем, каждый запрос на считывание, сделанный из Reader, вызывает запрос соответствующего считывания из базового символа или потока байтов. Поэтому рекомендуется обернуть BufferedReader вокруг любого считывателя, чьи операции read () могут быть дорогостоящими, например FileReaders и InputStreamReaders. Например,

BufferedReader in = new BufferedReader(new FileReader("foo.in")); Будет буферизовать ввод из указанного файла. Без буферизации каждый вызов read () или readLine () может привести к чтению байтов из файла, преобразованию в символы и возврату, что может быть очень неэффективным.

Итак, я понимаю, что InputStream может читать только один байт, InputStreamReader – один символ, а BufferedReader – целую строку и что он также делает что-то об эффективности, чего я не получаю. Я хотел бы лучше понять, кто что делает, чтобы понять, почему мне нужны все трое, и какая разница была бы без одного из них.

Я много исследовал здесь и в других местах в Интернете и, похоже, не нашел объяснений по этому поводу, которые я могу понять, почти все учебники просто повторяют информацию о документации. Вот некоторые связанные вопросы, которые, возможно, начнут объяснять это, но не углубляйтесь и не решайте мою путаницу: Q1 , Q2 , Q3 , Q4 . Я думаю, что это может иметь отношение к объяснению последнего вопроса о системных вызовах и возврате. Но я хотел бы понять, что подразумевается под этим.

Может быть, readLine () BufferedReader вызывает метод read () InputStreamReader, который, в свою очередь, вызывает метод read () метода InputStream? И InputStream возвращает байты, преобразованные в int, возвращая один байт за раз, InputStreamReader достаточно читает их, чтобы сделать один символ и преобразовать его в int и возвращает один символ за раз, а BufferedReader читает достаточно этих символов Представлены как целые числа, чтобы составить целую строку? И возвращает всю строку как строку, возвращающуюся только один раз, а не несколько раз? Я не знаю, я просто пытаюсь понять, как все работает.

Большое спасибо заранее!

    Это потоки в концепциях Java и ссылке на использование , дают очень хорошие объяснения.

    Эта

    Streams, Readers, Writers, BufferedReader, BufferedWriter – это терминология, с которой вы будете иметь дело на Java. Есть классы, предоставляемые на Java для работы с вводом и выводом. На самом деле стоит знать, как они связаны и как они используются. Эта статья будет подробно изучать потоки в Java и других связанных классах. Итак, давайте начнем:

    Определим каждый из них на высоком уровне, а затем углубимся.

    Streams
    Используется для обработки данных уровня байта

    Устройство чтения / записи
    Используется для обозначения уровня персонажа. Он также поддерживает различную кодировку символов.

    BufferedReader / BufferedWriter
    Повысить производительность. Данные для чтения будут буферизированы в память для быстрого доступа.

    Хотя они предназначены для ввода данных, для соответствующих результатов также существуют соответствующие классы. Например, если есть InputStream, который предназначен для чтения потока байтов, а OutputStream поможет записать поток байтов.

    InputStreams
    Существует много типов ввода InputStreams java. Каждый подключается к различным источникам данных, таким как массив байтов, файл и т. Д.

    Например, FileInputStream подключается к источнику данных файла и может использоваться для чтения байтов из файла. Хотя ByteArrayInputStream может использоваться для обработки байтового массива в качестве входного потока.

    OutputStream
    Это помогает записывать байты в источник данных. Почти для каждого InputStream существует соответствующий OutputStream, где бы он ни был понятен.


    ОБНОВИТЬ

    Что такое буферизованный поток?

    Здесь я цитирую из буферизованных потоков , документации Java (с техническим объяснением):

    Буферизованные потоки

    Большинство примеров, которые мы видели до сих пор, используют небуферизованный ввод-вывод. Это означает, что каждый запрос на чтение или запись обрабатывается непосредственно базовой ОС. Это может сделать программу намного менее эффективной, поскольку каждый такой запрос часто запускает доступ к диску, сетевую активность или некоторые другие операции, которые относительно дороги.

    Чтобы уменьшить этот накладные расходы, платформа Java реализует буферизованные потоки ввода-вывода. Буферизованные входные потоки считывают данные из области памяти, известной как буфер; API-интерфейс native input вызывается только тогда, когда буфер пуст. Аналогично, буферизованные выходные потоки записывают данные в буфер, а собственный API вывода вызывается только тогда, когда буфер заполнен.

    Иногда я теряю свои волосы, читая техническую документацию. Итак, здесь я цитирую более гуманное объяснение из https://yfain.github.io/Java4Kids/ :

    В общем, доступ к диску намного медленнее, чем обработка, выполняемая в памяти; Поэтому не рекомендуется обращаться к диску тысячу раз, чтобы прочитать файл размером 1000 байт. Чтобы минимизировать количество обращений к диску, Java предоставляет буферы, которые служат резервуарами данных.

    Введите описание изображения здесь

    При чтении файла с FileInputStream, затем BufferedInputStream, класс BufferedInputStream работает как посредник между FileInputStream и самим файлом. Он считывает большой кусок байтов из файла в память (буфер) за один снимок, а затем объект FileInputStream считывает из него отдельные байты, которые являются быстрыми операциями памяти с памятью. BufferedOutputStream работает аналогично классу FileOutputStream.

    Основная идея здесь – минимизировать доступ к диску. Буферизованные потоки не меняют тип исходных потоков – они просто делают чтение более эффективным. Программа выполняет поточную цепочку (или потоковый конвейер) для подключения потоков, так же, как трубы подключены к сантехнике.

    • 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 предназначен для ввода из файла , а BufferedReader использует буфер памяти, поэтому фактическое физическое считывание не считывает характер (неэффективно). С помощью new BufferedReader(otherReader) вы добавляете буферизацию к вашему оригинальному читателю.

    Все это понятно, есть класс утилиты Files с такими методами, как newBufferedReader(Path, Charset) которые добавляют дополнительную краткость.