1. Клас OutputStream
З потоками вводу ми щойно розібралися. Тепер саме час поговорити про потоки виводу.
Клас OutputStream є батьківським класом для всіх класів, які підтримують байтове виведення. Це абстрактний клас, який сам нічого не робить: для цього є класи-спадкоємці на всі випадки.
Дещо складно, чи не так? Якщо простіше, цей клас працює з байтами, а не, скажімо, із символами чи іншими типами даних. А термін «абстрактний клас» означає, що зазвичай використовується не цей клас, а класи-спадкоємці, наприклад FileOutputStream і подібні.
Але повернемося до класу OutputStream. Цей клас має методи, що їх зобов'язані реалізовувати всі його класи-спадкоємці. Перелічимо основні з них:
| Методи | Опис |
|---|---|
|
Записує один байт (не int) у потік. |
|
Записує масив байтів у потік |
|
Записує частину масиву байтів у потік |
|
Записує в потік усі дані, які є в буфері |
|
Закриває потік |
Під час створення об'єкта класу-спадкоємця 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() можна не викликати.
Приклад: копіювання файлу
| Код | Примітка |
|---|---|
|
InputStream для читання з файлуOutputStream для запису у файлБуфер, в який зчитуються дані Доки в потоці є дані Зчитуємо дані в буфер Записуємо дані з буфера в другий потік |
2. Клас Writer
Клас Writer — це повний аналог класу OutputStream, тільки він працює не з байтами, а із символами — char.
Це абстрактний клас: об'єкти класу Writer створити неможливо. Його головне призначення — бути єдиним батьківським класом для сотень класів-спадкоємців і передати їм спільні методи роботи із символьними потоками.
Методи класу Writer (і всіх його класів-спадкоємців):
| Методи | Опис |
|---|---|
|
Записує один символ (не int) у потік. |
|
Записує масив символів у потік |
|
Записує частину масиву символів у потік |
|
Записує рядок у потік |
|
Записує частину рядка в потік |
|
Записує в потік усі дані, які є в буфері |
|
Закриває потік |
Методи дуже схожі на методи класу 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() можна не викликати.
Приклад програми, яка копіює текстовий файл:
| Код | Примітка |
|---|---|
|
Reader для читання з файлуWriter для запису у файлБуфер, в який зчитуються дані Доки в потоці є дані Зчитуємо дані в буфер Записуємо дані з буфера в другий потік |
Класс StringWriter
StringWriter — це ще один цікавий клас-спадкоємець класу Writer. Він містить змінюваний рядок — об'єкт StringBuffer. І щоразу, коли ви щось «пишете» в об'єкт StringWriter, текст просто додається у внутрішній буфер.
Приклад:
| Код | Примітка |
|---|---|
|
Створюється цільовий символьний потік StringWriterРядок записується в буфер усередині StringWriterРядок записується в буфер усередині StringWriterПеретворюємо вміст об'єкта на рядок |
У цьому коді клас StringWriter по суті є обгорткою класу StringBuffer, однак клас StringWriter — це спадкоємець класу-потоку Writer і може використовуватися в ланцюжках з об'єктів-потоків. Доволі корисна властивість на практиці.
3. Клас PrintStream
Класи потокового виводу теж можна об'єднувати в ланцюжки з використанням потоків-посередників, які записують дані в переданий їм цільовий потік. Загальна схема взаємодії цих потоків така:

PrintStream — це найцікавіший з усіх проміжних потоків виводу, який до того ж має найбільше функцій. Він має кілька десятків методів і аж 12 конструкторів.
Клас PrintStream є спадкоємцем класу FilterOutputStream, а той — спадкоємцем класу OutputStream. Тому клас PrintStream має всі методи батьківських класів і до того ж свої власні. Наведемо найцікавіші з них:
| Методи | Опис |
|---|---|
|
Перетворює переданий об'єкт на рядок і виводить у цільовий потік. |
|
Перетворює переданий об'єкт на рядок і виводить у цільовий потік. В кінці додає символ розриву рядка. |
|
Виводить у цільовий потік символ розриву рядка. |
|
Конструює та виводить рядок на основі рядка-шаблону й переданих аргументів подібно до методу String.format(). |
А де ж кілька десятків методів, запитаєте ви?
Річ у тім, що цей клас має багато варіантів методів print() і println() з різними аргументами, які вже описано в таблиці вище.
Ми навіть не розглядатимемо ці методи, тому що ви їх і так вже добре знаєте. Здогадуєтеся, до чого я хилю?
Пам'ятаєте команду System.out.println()? Її можна записати у два рядки:
| Код | Виведення на екран |
|---|---|
|
|
Наша улюблена команда System.out.println() — це виклик методу println() для статичної змінної out класу System. І ця змінна має тип PrintStream.
На всіх попередніх рівнях ви майже в кожній задачі викликали методи класу PrintStream і навіть не здогадувалися про це!
Практичне використання
У мові Java є один цікавий клас — ByteArrayOutputStream, успадкований від OutputStream, і цей клас, власне, є масивом байтів, що динамічно збільшується.
Об'єкт ByteArrayOutputStream і об'єкт PrintStream можна об'єднати в такий ланцюжок:
| Код | Опис |
|---|---|
|
Створили в пам'яті буфер для запису Обгорнули буфер об'єктом PrintStreamЗаписали дані як у консоль Перетворили масив на рядок! Виведення на екран: |
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ