JavaRush /Курсы /Java Syntax Pro /Цепочки потоков

Цепочки потоков

Java Syntax Pro
15 уровень , 2 лекция
Открыта

1. Класс InputStreamReader

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

Это очень мощный механизм в Java, который позволил конструировать сложные сценарии чтения данных, соединяя одни потоки с другими. Выглядит такая схема примерно так:

Класс InputStreamReader

Когда программа читает данные из потока данных, он в свою очередь читает их из своего источника данных: другого потока данных или из файла, например.

При этом каждый поток данных не просто читает и отдает данные, а еще может преобразовывать их или выполнять над ними различные операции. Хорошим примером такого «промежуточного потока» данных является класс InputStreamReader.

Мы уже знаем класс, который называется FileReader, и это Reader, который читает данные из файла. А откуда читает данные класс InputStreamReader? Правильно: из потока InputStream.

Чтобы создать объект типа InputStreamReader, в него нужно передать объект типа InputStream, или его класса наследника. Пример:

String src = "c:\\projects\\log.txt";
FileInputStream input = new FileInputStream(src);
InputStreamReader reader = new InputStreamReader(input);

У класса InputStreamReader есть все методы, которые есть у класса Reader, причем они работают точно так же.

Основное отличие классов InputStreamReader и, например, FileReader в том, откуда они читают данные. FileReader читает данные из файла (поэтому он и называется FileReader), а InputStreamReader читает данные из потока InputStream.

Когда вы читаете символ из объекта FileReader с помощью метода read(), он в свою очередь читает из файла на диске два байта и возвращает их вам как char.

Когда вы читаете символ из объекта InputStreamReader с помощью метода read(), он в свою очередь читает два байта из переданного в него объекта FileInputStream, который в свою очередь читает данные из файла. Получается такая цепочка вызовов методов read().


2. Класс BufferedReader

Есть еще один интересный класс, который вы, скорее всего, будете часто использовать — BufferedReader. Это тоже «промежуточный поток», который читает данные из другого потока.

Класс BufferedReader, как видно из его названия, является классом-наследником от Reader и позволяет читать символы. Однако, что самое интересное, в качестве источника данных в него тоже нужно передать поток, из которого можно читать символы – поток-наследник от класса Reader.

В чем же смысл? В отличие от InputStreamReader’а, класс BufferedReader не преобразовывает байты в символы: он вообще ничего не преобразовывает. Вместо этого он буферизует данные.

Класс BufferedReader

Когда программа читает из объекта BufferedReader один символ, он читает из своего потока-источника сразу большой массив символов. И сохраняет их у себя внутри.

При чтении следующего символа из объекта BufferedReader, он просто возьмет очередной символ из своего внутреннего массива-буфера и отдаст его, не обращаясь при этом к потоку-источнику данных. И только когда все символы в буфере закончатся, он снова прочитает большой массив символов.

Еще у класса BufferedReader есть очень полезный метод — String readLine(), который позволяет читать данные из потока-источника сразу строками. С помощью этого метода можно, например, прочитать какой-то файл и вывести его содержимое на экран построчно. Пример:

Мы специально записали код в компактном формате, чтобы вы увидели, как это может быть удобно. Можно записать данный код и немного более детально.

String src = "c:\\projects\\log.txt";

try(FileReader in = new FileReader(src);
BufferedReader reader = new BufferedReader(in))
{
   while (reader.ready())
   {
      String line = reader.readLine();
      System.out.println(line);
   }
}


Создаем объект FileReader, источник данных — файл.
Создаем объект BufferedReader, источник данных — объект FileReader;
пока в reader еще есть данные

Прочитать одну линию
Вывести линию на экран
Важный момент:

Если вы соединили несколько потоков в цепочку, метод close() достаточно вызвать только у одного из них: он вызовет его у своего источника данных и т.д., пока не дойдут до финального потока с данными.



3. Чтение с консоли

И еще один интересный факт: класс Scanner — ничто иное как входящий промежуточный поток данных, который читает их из потока System.in — тоже потока данных.

Вот два способа чтения строки с консоли:

Класс Scanner Классы BufferedReader и InputStreamReader
InputStream stream = System.in;
Scanner console = new Scanner(stream);
String line = console.nextLine();
InputStream stream = System.in;
InputStreamReader reader = new InputStreamReader(stream);
BufferedReader buff = new BufferedReader(reader);
String line = buff.readLine();

Наш знаменитый System.in — это ничто иное как статическая переменная in класса System. Тип ее — InputStream, а имя — in.

Так что практически с самого начала изучения Java на JavaRush вы работаете с потоками данных и строите из них цепочки. Только теперь вы будете это делать более осознанно.


Комментарии (403)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Grigoryvvv Уровень 17 Expert
31 марта 2026
31.03.2026 / 16 уровень ● Схема байтовых и символьных потоков java.io СИМВОЛЫ (char) БАЙТЫ (byte) ══════════════════════════════════════════════════════════ АБСТРАКЦИЯ ЧТЕНИЯ/ЗАПИСИ: [READER] | [WRITER] | [INPUTSTREAM] | [OUTPUTSTREAM] чтение символов | запись символов | чтение байтов | запись байтов ══════════════════════════════════════════════════════════ КОНКРЕТНЫЕ РЕАЛИЗАЦИИ: РАБОТА С ФАЙЛОМ [FileReader] | [FileWriter] | [FileInputStream] | [FileOutputStream] читает файл | пишет файл | читает файл | пишет файл (символы) | (символы) | (байты) | (байты) ══════════════════════════════════════════════════════════ БУФЕРИЗАЦИЯ: [BufferedReader] | [BufferedWriter] | [BufferedInputStream] | [BufferedOutputStream] readLine() | newLine() | быстрее чтение | быстрее запись ══════════════════════════════════════════════════════════ АДАПТЕРЫ (мост между байтами и символами): [InputStreamReader] → байты → символы (декодирование) [OutputStreamWriter] → символы → байты (кодирование) ● Чтение данных из файла через поток, запись данных в файл через поток Все файлы хранят данные в байтах. Любой файл - просто набор байтов "Текстовый файл" - это файл, байты которого можно интерпретировать как символы с помощью кодировки (UTF-8, UTF-16, Windows-1251, ISO-8859-1) Поэтому текстовый файл — это условное понятие. Расширения текстовых файлов .txt, .java, .xml, .html, .json, .csv, ..
Grigoryvvv Уровень 17 Expert
31 марта 2026
"Бинарный" файл - это файл, байты которого нельзя корректно и не следует интерпретировать как текст. Бинарный файл тоже условное понятие. Примеры расширений бинарных файлов .jpg, .png, .mp4, .exe, .zip, ... FileReader → открывает поток символьного чтения из текстового файла, декодируя байты через кодировку (кодировка системная по умолчанию) в символы. FileReader под капотом использует: FileInputStream → InputStreamReader (декодирование). FileWriter → открывает поток символьной записи, кодируя символы в байты и записывая их в файл. FileWriter под капотом использует: OutputStreamWriter (кодирование) → FileOutputStream. Важно: FileReader/FileWriter не подходят для бинарных файлов, поскольку байты бинарных файлов не нужно декодировать в символы. FileReader / FileWriter: они не позволяют указать кодировку. Используется кодировка операционной системы, в которой запущена программа. Например, на Windows по умолчанию будет Windows-1251. Если файлы изначально в UTF-8, чтение или запись через FileReader/FileWriter приведёт к "кракозябрам" вместо текста. FileInputStream → открывает поток байтового чтения из текстового или бинарного файла, не выполняя декодирования. FileOutputStream → открывает поток байтовой записи в текстовый или бинарный файл, не выполняя кодирования. ● Адаптеры Эти классы оборачивают байтовые потоки и преобразуют байты в символы и наоборот: InputStreamReader — декодирует байтовый поток в символьный OutputStreamWriter — кодирует символьный поток в байтовый Большой плюс в том, что InputStreamReader / OutputStreamWriter позволяют указывать кодировку, что безопасно для обработки текстовых файлов.

String src = "c:\\projects\\log.txt";
FileInputStream input = new FileInputStream(src);
InputStreamReader reader = new InputStreamReader(input, "UTF-8");
Grigoryvvv Уровень 17 Expert
31 марта 2026
Для работы с текстовыми файлами: ❌ Не рекомендуется: текст. файл → FileReader (небезопасно, кодировка системная) ✅ Рекомендуется: текст. файл → FileInputStream → InputStreamReader (безопасно, с явной кодировкой) Что бы узнать кодировку текстового файла, его надо открыть любым текстовым редактором (блокнот). В нижнем правом углу будет указана кодировка файла. ● Буферизация 1 Классы Buffered добавляют буферизацию (сами создают внутренний буфер). Буферизация — это использование временного хранилища (буфера) в памяти. Ключевое: буфер создается программой во время выполнения и находится в оперативной памяти (RAM) компьютера. Без буфера: каждый read() → обращение к диску (очень медленно) 😱 а если наоборот большой файл загрузить целиком в память readAllBytes() → перегружаем память 🤯 С буфером: Чтение и запись выполняются блоками, а не по одному байту/символу. 1 раз читаем большой кусок с диска → кладём в RAM → дальше читаем из памяти (быстро) ✅ Уменьшается количество обращений к диску ✅ Повышается производительность работы с файлами 2 Классы Buffered добавляют функциональность с помощью методов, без изменения самого потока. Ты "оборачиваешь" один поток другим, добавляя поведение. К примеру: BufferedReader → добавляет: -буфер -метод readLine(), // вот здесь добавляет функциональность 👉 Поэтому почти всегда используем: BufferedReader / BufferedWriter — для символов, ускоряют работу с большими файлами, удобны для чтения/записи построчно (readLine(), newLine()) (на вход принимают только символы) BufferedInputStream / BufferedOutputStream — для байтов, ускоряют работу с большими файлами (на вход принимают только байты) бинарный файл → FileInputStream → BufferedInputStream текстовый файл → FileReader → BufferedReader (❌ Не рекомендуется, небезопасно, кодировка системная) текстовый файл → FileInputStream → InputStreamReader → BufferedReader (✅ Рекомендуется, с явной кодировкой)
Grigoryvvv Уровень 17 Expert
31 марта 2026
Буфер всегда ставится "снаружи" для ускорения. Буфер всегда оборачивает самый "внешний" поток! Ну и зачем мне класс BufferedReader?, если я в классе FileReader могу использовать метод int read(char[] buffer) и создавать свой буфер в цикле, обращаясь к диску по мере использования? Конечно, можно. Но BufferedReader лучше по многим причинам: - Меньше кода и меньше ошибок; - Не нужно самим создавать буфер и выбирать его размер; - Не нужно следить за тем, сколько данных записано в буфер, чтобы случайно не обработать лишние байты/символы в конце и не записать "мусор", если данные в потоке закончатся и буфер будет не заполнен до конца; - Удобно оборачивать другие потоки; - Есть удобный метод readLine() для построчного чтения текста. Вам не надо самим вручную собирать строку по символам; - Если мы к примеру несколько раз вызовем метод read() у класса FileReader, то каждый раз метод будет обращаться напрямую к диску и читать один символ. Если же мы несколько раз вызовем метод read() у потока обернутого в класс BufferedReader, то сначала класс BufferedReader создаст внутренний буфер обратившись напрямую к диску, а далее каждый вызов read() будет брать по одному символу из буфера находящегося в памяти RAM.
Raymon4k 234 Уровень 12
18 мая 2026
местный Бетмен снова разложил все по полочкам, респект
AlexSa4kov Уровень 25
11 февраля 2026
Меня одного тревожит, что мы изучаем FileReader и BufferedReader, но в практике нам настоятельно ограничивают использование этих знаний и настаивают на Классе Files, который в лекциях не упоминался?🤔🤔🤔
Romaha Уровень 17
8 февраля 2026
В первой задачке не пропускало решение по второму пункту лишь из-за дополнительной строки: System.out.println("Введите путь к файлу: "); Спасибо за внимание. 🙂🙂🙂
C0N5P1RACY Уровень 1
14 января 2026
Еще раз повторюсь. Ранее я забил и скипнул весь этот уровень из за непонимания, я закончил весь первый квест, написал сапера, и теперь, вернувшись снова сюда что бы закрыть этот уровень, я стал понимать что тут вообще происходит, а задачи решаются на удивление легко. Если вам сложна эта тема - скипайте ее без сожаления, просто прочтите, особо не вникая в контекст, и скипайте. Эта тема действительно слишком рано подана, хронология понимания нарушена, просто идите дальше, и вернитесь потом, когда станете немного лучше понимать как что устроено. Займитесь ООП, напишите игру, по практикуйтесь с временем, классами, методами, абстракцией и тд, забудьте о потоках, они вам пока что только мешают. Это безусловно важная тема которая будет нужна потом, но сейчас - вам она будет только мешать концентрироваться на действительно важных базовых вещах.
Артем Чехов Уровень 16
18 сентября 2025
К чему была задача "Пропускаем не всех"? Нам рассказали про буферы, но мы не будем использовать их в практике, нужно использовать именно Files.readAllLines(). Так еще и принципиально нельзя использовать foreach списка, а нужно использовать итерацию через for. К чему такие жесткие ограничения?
25 сентября 2025
Действительно, очень странно... Думаю перейти на курс Java 25
Underdante Уровень 46
9 марта 2026
я решил через FileReader и внешний счетчик.
YoungDV Уровень 26
24 августа 2025
ЧИТАЕМ, ЗАБЫВАЕМ, ПРАКТИКУЕМСЯ, ТУПИМ, ПЕРЕЧИТАЕМ, ВСПОМИНАЕМ, ОПЯТ ЗАБЫВАЕМ . ЭТО ЧЕЛОВЕЧЕСКИЙ CPU, RAM И SSD. ГЛАВНОЕ НЕ СДАВАТЬСЯ!
NinaInfinity Уровень 21
24 июля 2025
Очень сложные задачи были в прошлой лекции, здесь все равно не особо поняла, но хотя бы лекции хватило чтобы решить задачи.
Raushan Уровень 16 Expert
23 июля 2025
Чет вообще перестал понимать, да и прошлую лекцию будто бы мимо пропустил((
25 сентября 2025
Все со временем прийдет, главное не останавливайся🤗
Anonymous #3585174 Уровень 33
15 июля 2025
Like
Сергей Сак Уровень 16
12 июля 2025

Мы уже знаем класс, который называется FileReader,
А мы уже знаем класс FileReader? По-моему в предыдущих лекциях ни слова о нем не было
4el0vek3 Уровень 40
17 июля 2025
В конце прошлой лекции были слова о нем