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

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

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

1. Клас InputStreamReader

Ще однією цікавою особливістю потоків є можливість об'єднувати кілька потоків у ланцюжки. Потік може читати дані не тільки із джерела даних, в якому вони зберігаються, але також з іншого потоку.

Це дуже потужний механізм у Java, який дав змогу конструювати складні сценарії читання даних, поєднуючи різні потоки. Ця схема має приблизно такий вигляд:

Коли програма читає дані з потоку даних, він своєю чергою читає їх зі свого джерела даних: з іншого потоку даних або, наприклад, із файлу.

Кожен потік даних не просто читає та віддає дані, а водночас може перетворювати їх або виконувати з ними різні операції. Наочним прикладом такого «проміжного потоку» даних є клас 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 має дуже корисний метод 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. А відтепер ви зможете це робити більш осмислено.


Коментарі (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
фух щось з кожним рівнем якось все більше не вкладається інфа в голові(