— Привіт! Зараз я розповім тобі про два інтерфейси – InputStream та OutputStream. Оголошені вони як абстрактні класи, але якщо придивитися до них уважніше, можна побачити, що за своєю суттю це інтерфейси. Майже всі їх методи абстрактні, окрім декількох незначних методів. Дуже схожі на нашого «охоронця», якого ми розглядали.

Це дуже цікаві інтерфейси. Поки що я спеціально називатиму їх інтерфейси, щоб ти зрозумів, навіщо вони потрібні. А потім ми поговоримо, чому їх все ж таки зробили абстрактними класами.

— Добре. То що це за інтерфейси?

— Зараз розповім.

Є така цікава річ у Java як «потік». Потік – це дуже проста сутність. І його простота є запорукою дуже потужного механізму обміну даними. Потоки бувають двох видів:  потік для читання і для запису.

У потік для запису, як ти вже, мабуть, здогадався, можна записувати дані. Для цього він має метод write(). З потоку для читання можна читати дані. Для цього він має метод read().

InputStream – це інтерфейс потоку читання, який описує таку здатність: «з мене можна читати байти».

А OutputStream – це, відповідно, інтерфейс потоку запису, що описує здатність: «в мене можна записувати байти».

— І це все?

— Фактично так. Але вся річ у тому, що Java має дуже багато класів, які вміють працювати з інтерфейсами InputStream і OutputStream. Наприклад, ти хочеш прочитати файл з диска та вивести його зміст на екран. Немає нічого простішого.

Для того, щоб прочитати дані з файлу на диску, є спеціальний клас FileInputStream, який реалізує інтерфейс InputStream. Хочеш записати прочитані дані в інший файл? Для цього є клас FileOutputStream, який реалізує інтерфейс OutputStream. Ось як виглядає код копіювання [даних одного] файлу в інший.

Код
public static void main(String[] args) throws IOException
{
 InputStream inStream = new FileInputStream("c:/source.txt");
 OutputStream outStream = new FileOutputStream("c:/result.txt");

 while (inStream.available() > 0)
 {
  int data = inStream.read(); //читаємо один байт із потоку для читання
  outStream.write(data); //записуємо прочитаний байт в інший потік.
 }

 inStream.close(); //закриваємо потоки
 outStream.close();
}

Уяви, що ми написали клас і додали йому здібності InputStream та OutputStream.

Якщо ми правильно реалізували підтримку цих інтерфейсів, об'єкти нашого класу тепер можна зберегти у файл на диску. Просто вичитати їх вміст через метод read. Або завантажити з файлу: для цього треба створити об'єкт і записати вміст файлу через метод write.

— А можна приклад?

— Можна.

Код Опис
class MyClass
{
private List<Integer> list =
 new ArrayList<>(Arrays.asList(111, 222, 333));
}
Уявимо, що наш клас містить один об'єкт – ArrayList типа Integer. Наповнимо список тестовими даними.

Тепер додамо до нього методи read та write

Код Опис
class MyClass
{
private List<Integer> list =
 new ArrayList<>(Arrays.asList(111, 222, 333));

public void write(int data)
{
list.add(data);
}

public int read()
{
int first = list.get(0);
list.remove(0);
return first;
}

public int available()
{
return list.size();
}
}
Тепер у нас у класі реалізовано метод read, який дозволяє послідовно вичитати весь вміст нашого списку list.

І метод write, який дозволяє записувати до нашого list значення.

Це, звичайно, не реалізація інтерфейсів InputStream та OutputStream, але дуже схоже.

— Так, це зрозуміло. А як все ж таки зберегти вміст такого об'єкта у файл?

— Давай я напишу тобі приклад:

Запис об'єкта MyClass у файл
public static void main(String[] args) throws IOException
{
 MyClass myObject = new MyClass();
 OutputStream outStream = new FileOutputStream ("c:/my-object-data.txt");

 while (myObject.available() > 0)
 {
  int data = myObject.read(); //читаємо один int з потоку для читання
  outStream.write(data); //записуємо прочитаний int в інший потік.
 }

 outStream.close();
}
Читання об'єкта MyClass із файлу
public static void main(String[] args) throws IOException
{
 InputStream inStream = new FileInputStream("c:/my-object-data.txt");
 MyClass myObject = new MyClass();

 while (inStream.available() > 0)
 {
  int data = inStream.read(); //читаємо один int з потоку для читання
  myObject.write(data); //записуємо прочитаний int в інший потік.
 }

 inStream.close(); //закриваємо потоки
}

— Овва! А й справді, дуже схоже на роботу з InputStream/OutputStream. Потоки – це крута річ!

— Ще б пак!