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()

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

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

Код Примітка
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()

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

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

Код Примітка
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

Є ще один цікавий клас-спадкоємець від класу Writer — це StringWriter. У ньому знаходиться рядок, що змінюється, — об’єкт 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, і він може використовуватися в ланцюжках із об’єктів-потоків. Доволі корисна властивість на практиці.