JavaRush /Курси /Java Syntax Zero /Ланцюжки потоків

Ланцюжки потоків

Java Syntax Zero
Рівень 16 , Лекція 3
Відкрита

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 на CodeGym ви працюєте з потоками даних і будуєте з них ланцюжки. Тільки тепер ви робитимете це більш усвідомлено.


Коментарі (17)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Роман Рівень 28
8 червня 2025
Получается класс System наследует класс InputStream? - ("Виявляється, наш знаменитий System.in — це статична змінна in класу System. Її тип — InputStream, а ім'я — in.")
Alexander Kisil Рівень 51
27 жовтня 2024
В попередній темі вчились користуватись try-catch, а виявилось що тут вже ними користуватись не можна ("Пропускаємо не всіх"), бо не пройде провірку. Можливо у розділі задачі, де написано чим не можна користуватись, також додавати "Не можна використовувати, те що ви проходили до цього". ((
IronMan57 Рівень 28
4 січня 2025
Наскільки зрозумів, в задачах до цієї та попередньої лекції потрібно використовувати не просто try-catch, а try-with-resources. Хоча, іноді не вгадаєш, що цьому валідатору не подобається. В першій задачі до цієї лекції ("Пропускаємо не всіх"), дійсно валідатор пропускав не всіх, наприклад моє рішення, в якому була стрічка-запрошення:

System.out.println("Enter the name of the source file: ");
Як тільки її закоментував - рішення було прийняте.
Viacheslav B. Рівень 1
27 березня 2024
ця лекція більш менш була зрозуміла, задачі не викликали багато запитань, а ось з попередньої були проблеми з вирішуванням задач. мабуть за мало інформації або практики...
Василь Рівень 4
27 вересня 2023
До опису класів Scanner і BufferedReader додали б коротку форму запису, а саме:

        Scanner scanner = new Scanner(System.in);
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
або б додавали лінки де можна почитати ще, якщо самі тему розкрити не можете.
Pavlo Kezin Рівень 23
22 вересня 2023
Просто напишу. Лекцією не задоволений.
Богдан Рівень 37
26 серпня 2023
чудова лекція! все зрозуміло.🤓😉
PAMPERS Рівень 26
13 серпня 2023
Я не зрозумів навіщо накидувати ще додатково теми, якщо задач мало на таку важку як я вважаю тему. Типу для засвоєння знань задач і так мало, так для вирішення задач які є треба додатково самим щось читати, все просто забувається.
Роман Рівень 94 Expert
16 червня 2023
Цілком підттримаю всі попередні коментарі, викладання не зрозуміле, деякі задачі можна вирішити трішки інакше але цілком в рамках данної теми, але вони не проходять, іноді просто смішно...
les_yeux_blancs Рівень 50
29 квітня 2023
Дуже туго подано, треба більш детально й більше прикладів, я зрозумів лише тому, що й так знав, як воно працює
Ivan Lebid' Рівень 108 Expert
28 квітня 2023
фух щось з кожним рівнем якось все більше не вкладається інфа в голові(