JavaRush /Курси /Java Syntax Zero /Потоки виводу

Потоки виводу

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

1. Клас OutputStream

З потоками вводу ми щойно розібралися. Тепер саме час поговорити про потоки виводу.

Клас OutputStream є батьківським класом для всіх класів, які підтримують байтове виведення. Це абстрактний клас, який сам нічого не робить: для цього є класи-спадкоємці на всі випадки.

Дещо складно, чи не так? Якщо простіше, цей клас працює з байтами, а не, скажімо, із символами чи іншими типами даних. А термін «абстрактний клас» означає, що зазвичай використовується не цей клас, а класи-спадкоємці, наприклад FileOutputStream і подібні.

Але повернемося до класу OutputStream. Цей клас має методи, що їх зобов'язані реалізовувати всі його класи-спадкоємці. Перелічимо основні з них:

Методи Опис
void write(int b)
Записує один байт (не int) у потік.
void write(byte[] buffer)
Записує масив байтів у потік
void write(byte[] buffer, off, len)
Записує частину масиву байтів у потік
void flush()
Записує в потік усі дані, які є в буфері
void close()
Закриває потік

Під час створення об'єкта класу-спадкоємця InputStream зазвичай указується об'єкт-джерело, з якого InputStream читає дані. Під час створення об'єкта класу-спадкоємця OutputStream також зазвичай указується цільовий об'єкт або цільовий потік, в який будуть записуватися дані.

Побіжно перегляньмо всі методи класу OutputStream:

Метод write(int b)

Цей метод записує в потік виводу один байт (не int). Передане значення перетворюється на байтовий тип, три перші байти відкидаються.

Метод write(byte[] buffer)

Записує в потік виводу переданий масив байтів.

Метод write(byte[] buffer, int offset, int length)

Записує в потік виводу частину переданого масиву байтів. Змінна offset задає номер першого елемента масиву, length — довжину записуваного фрагмента.

Метод flush()

Метод flush() використовується для примусового запису в цільовий потік таких даних, які можна кешувати в цьому потоці. Застосовується в разі використання буферизації та/або декількох об'єктів-потоків, об'єднаних у ланцюжок.

Метод close()

Записує в цільовий об'єкт усі незаписані дані. У разі використання оператора try-with-resources метод close() можна не викликати.

Приклад: копіювання файлу

Код Примітка
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStream для читання з файлу
OutputStream для запису у файл

Буфер, в який зчитуються дані
Доки в потоці є дані

Зчитуємо дані в буфер
Записуємо дані з буфера в другий потік

2. Клас Writer

Клас Writer — це повний аналог класу OutputStream, тільки він працює не з байтами, а із символами — char.

Це абстрактний клас: об'єкти класу Writer створити неможливо. Його головне призначення — бути єдиним батьківським класом для сотень класів-спадкоємців і передати їм спільні методи роботи із символьними потоками.

Методи класу Writer (і всіх його класів-спадкоємців):

Методи Опис
void write(int b)
Записує один символ (не int) у потік.
void write(char[] buffer)
Записує масив символів у потік
void write(char[] buffer, off, len)
Записує частину масиву символів у потік
void write(String str)
Записує рядок у потік
void write(String str, off, len)
Записує частину рядка в потік
void flush()
Записує в потік усі дані, які є в буфері
void close()
Закриває потік

Методи дуже схожі на методи класу OutputStream, тільки працюють не з байтами, а із символами.

Короткий опис методів:

Метод write(int b)

Цей метод записує в потік виводу один символ char (не int). Передане значення перетворюється на тип char, два перші байти відкидаються.

Метод write(char[] buffer)

Записує в потік виводу переданий масив символів.

Метод write(char[] buffer, int offset, int length)

Записує в потік виводу частину переданого масиву символів. Змінна offset задає номер першого елемента масиву, length — довжину записуваного фрагмента.

Метод write(String str)

Записує в потік виводу переданий рядок.

Метод write(String str, int offset, int length)

Записує в потік виводу частину переданого рядка й перетворює рядок на масив символів. Змінна offset задає номер першого елемента масиву, length — довжину записуваного фрагмента.

Метод flush()

Метод flush() використовується для примусового запису в цільовий потік таких даних, які можна кешувати в цьому потоці. Застосовується в разі використання буферизації та/або декількох об'єктів-потоків, об'єднаних у ланцюжок.

Метод close()

Записує в цільовий об'єкт усі незаписані дані. У разі використання оператора try-with-resources метод close() можна не викликати.

Приклад програми, яка копіює текстовий файл:

Код Примітка
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Reader для читання з файлу
Writer для запису у файл

Буфер, в який зчитуються дані
Доки в потоці є дані

Зчитуємо дані в буфер
Записуємо дані з буфера в другий потік

Класс StringWriter

StringWriter — це ще один цікавий клас-спадкоємець класу Writer. Він містить змінюваний рядок — об'єкт StringBuffer. І щоразу, коли ви щось «пишете» в об'єкт StringWriter, текст просто додається у внутрішній буфер.

Приклад:

Код Примітка
StringWriter writer = new StringWriter();
writer.write("Hello");
writer.write(String.valueOf(123));

String result = writer.toString();
Створюється цільовий символьний потік StringWriter
Рядок записується в буфер усередині StringWriter
Рядок записується в буфер усередині StringWriter

Перетворюємо вміст об'єкта на рядок

У цьому коді клас StringWriter по суті є обгорткою класу StringBuffer, однак клас StringWriter — це спадкоємець класу-потоку Writer і може використовуватися в ланцюжках з об'єктів-потоків. Доволі корисна властивість на практиці.



3. Клас PrintStream

Класи потокового виводу теж можна об'єднувати в ланцюжки з використанням потоків-посередників, які записують дані в переданий їм цільовий потік. Загальна схема взаємодії цих потоків така:

Клас PrintStream

PrintStream — це найцікавіший з усіх проміжних потоків виводу, який до того ж має найбільше функцій. Він має кілька десятків методів і аж 12 конструкторів.

Клас PrintStream є спадкоємцем класу FilterOutputStream, а той — спадкоємцем класу OutputStream. Тому клас PrintStream має всі методи батьківських класів і до того ж свої власні. Наведемо найцікавіші з них:

Методи Опис
void print(obj)
Перетворює переданий об'єкт на рядок і виводить у цільовий потік.
void println(obj)
Перетворює переданий об'єкт на рядок і виводить у цільовий потік. В кінці додає символ розриву рядка.
void println()
Виводить у цільовий потік символ розриву рядка.
PrintStream format(String format, args...)
Конструює та виводить рядок на основі рядка-шаблону й переданих аргументів подібно до методу String.format().

А де ж кілька десятків методів, запитаєте ви?

Річ у тім, що цей клас має багато варіантів методів print() і println() з різними аргументами, які вже описано в таблиці вище.

Ми навіть не розглядатимемо ці методи, тому що ви їх і так вже добре знаєте. Здогадуєтеся, до чого я хилю?

Пам'ятаєте команду System.out.println()? Її можна записати у два рядки:

Код Виведення на екран
PrintStream stream = System.out;
stream.println("Hello!");
Hello!

Наша улюблена команда System.out.println() — це виклик методу println() для статичної змінної out класу System. І ця змінна має тип PrintStream.

На всіх попередніх рівнях ви майже в кожній задачі викликали методи класу PrintStream і навіть не здогадувалися про це!

Практичне використання

У мові Java є один цікавий клас — ByteArrayOutputStream, успадкований від OutputStream, і цей клас, власне, є масивом байтів, що динамічно збільшується.

Об'єкт ByteArrayOutputStream і об'єкт PrintStream можна об'єднати в такий ланцюжок:

Код Опис
ByteArrayOutputStream baos = new ByteArrayOutputStream();

try(PrintStream stream = new PrintStream(baos))
{
   stream.println("Hello");
   stream.println(123);
}

String result = baos.toString();

System.out.println(result);
Створили в пам'яті буфер для запису

Обгорнули буфер об'єктом PrintStream

Записали дані як у консоль



Перетворили масив на рядок!

Виведення на екран:
Hello!
123

Коментарі (24)
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ
Ва Дим Рівень 28
28 квітня 2024
Згідний з коментами що до останньої задачі .Допомогло в рішенні перевертання строки пере додаванням в метод.можлиов ще якісь є варіанти але вже лінь думати ))
Василь Рівень 4
28 вересня 2023
я думав я почав тупіти, але матеріал подається вкрай паршиво. Окрім незрозумілих умов задач ще і валідатор вимагає рішення котрі незрозумілі студенту і логічні з точки зору вирішення задачі. Перечитав коментарі і виявляється я не один такий. Так що не зупиняйтеся, гризіть граніт далі. Але приділяйте увагу зовнішньому матеріалу.
24 червня 2023
Поясніть, будь ласка, що означає "кешувати"
Vlad Рівень 17
8 грудня 2023
Кешування (від англ. "caching") - це техніка зберігання копій даних чи обчислень для подальшого їхнього використання, зазвичай з метою прискорення доступу або зменшення використання ресурсів. Коли ви кешуєте дані, ви фактично зберігаєте їх тимчасово у вигляді копій на швидкодіїючому носії, такому як оперативна пам'ять чи швидкий доступ до диска. gpt😅
les_yeux_blancs Рівень 50
29 квітня 2023
Остання задача неймовірно погано описана й задизайнена, це просто жах, там взагалі незрозуміло, що робити
Unfo Рівень 23
11 лютого 2023
Поки що найгріший блок за інформативністю та подачею матеріалу.
unite Рівень 24 Expert
25 січня 2023
Остання задача outputStream.reset();, де в лекціє про цей метод щось?
oleh ronin Рівень 47
8 листопада 2023
В тому то й діло, щоб знайти самому, JavaRush тобі не буде шукати і давати готовий код, коли ти будеш працювати в якісь компанії)
17 лютого 2024
І нафіга я тоді 100$ віддав? Щоб бігати шукати інфу? Так я міг і в ютубі повчити
oleh ronin Рівень 47
17 лютого 2024
Вчи в юутубі, раз ти не зрозумів такий підхід від JavaRush
17 лютого 2024
Топове рішення, так і роблю
kalkulator¹ Рівень 51
20 листопада 2022
доволі складна тема, дуже важко засвоюється в голові притому, що я подивися 6 відеороликів по ній((((
WhoAMI Рівень 51
11 листопада 2022
як зрозуміти ось це: void write(int b) - Записує один байт (не int) у потік. А що тооді записується якщо не int?
24 червня 2023
Відбувається те саме, що робить оператор "(byte)". Тобто запишеться int який ми передаємо в якості аргумента, але переконвертований до типу byte. Звичайно з можливими втратами(якщо значення за межами можливих значень типу byte). Сам не розумію чому не можна було зробити щоб метод приймав тільки байти.
FAUST_ua Рівень 29
10 вересня 2022
Для вирішення останньої задачі - перечитайте про StringBuilder (Java Syntax Zero) 10 рівень, 7 лекція.
kalkulator¹ Рівень 51
20 листопада 2022
бляха навіть перечитав перед лекцією твій комент але згадав, коли почав робити задачу через цикл for
Beisik Рівень 25
24 грудня 2022
Спасибі тобі
15 січня 2023
Вельми вдячний, без цієї підказки б не вирішив.
Василь Рівень 4
28 вересня 2023
Використовував саме StringBuilder, але валідатор всеодно сварився. Почав через список, але і так не вийшло, тепер натрапив на ваш коментар. Чи могли б ви більше детально описати своє рішення. Чи що саме читати?
Viacheslav B. Рівень 1
28 березня 2024
щиро дякую!
Viacheslav B. Рівень 1
28 березня 2024
Василь я зробив так я одразу передав у ByteArrayOutputStream рядок правильного формата (перевернути рядок ) зробив це за допомогою StringBuilder
Yurii Sakharuk Рівень 27
25 травня 2022
Жахливий весь блок про ІО. Інформація подається непослідовно, перескакуєте від класу до класу без послідовності та нормального пояснення, частина матеріалу не актуальна, задачі не відповідають інформації з лекцій.
4 липня 2022
Братушанічка, +100 тобі до карми. Я вже думав - торба, отупєл... Все виходило, а тут забуксував. Блок мабуть найважчий з пройдених
Pavlo Kezin Рівень 23
22 вересня 2023
це вже не навчання, а просто збірник задач. у лекціях просто аби що... "гугліть, як справжні про".