JavaRush /Курсы /JAVA 25 SELF /ObjectOutputStream, ObjectInputStream: работа с потоками

ObjectOutputStream, ObjectInputStream: работа с потоками

JAVA 25 SELF
42 уровень , 3 лекция
Открыта

1. Введение

В Java сериализация работает только с теми объектами, которые явно разрешили это делать. Для этого класс должен реализовать специальный интерфейс — java.io.Serializable.

import java.io.Serializable;

public class Person implements Serializable {
    // Поля, конструкторы, методы
}

Serializable — это маркерный интерфейс: у него нет методов, он просто сообщает JVM — «этот класс можно сериализовать, не бойтесь!». Если вы попробуете сериализовать объект класса, который не реализует Serializable, получите исключение NotSerializableException. Даже если хотя бы одно поле (или вложенный объект) не сериализуемо — сериализация не сработает.

ObjectOutputStream и ObjectInputStream

  • ObjectOutputStream — класс, который записывает объекты в поток (например, в файл или по сети).
  • ObjectInputStream — класс, который читает объекты из потока.

Они работают в паре: один сериализует объект, другой — десериализует.

Основные методы

  • writeObject(Object obj) — сериализует объект и записывает его в поток.
  • readObject() — читает объект из потока, десериализует и возвращает его.

Важно: оба класса работают поверх обычных потоков ввода-вывода (OutputStream и InputStream). Чаще всего их используют вместе с файловыми потоками — FileOutputStream и FileInputStream, но можно применять и с сетевыми потоками.

2. Пример сериализации

Давайте напишем простой пример: сериализуем и десериализуем объект класса Person.

Шаг 1. Описываем класс

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;
    // transient-поле не будет сериализовано
    private transient String secret;

    public Person(String name, int age, String secret) {
        this.name = name;
        this.age = age;
        this.secret = secret;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + ", secret='" + secret + "'}";
    }
}

Шаг 2. Сериализация объекта в файл

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;

public class SerializeDemo {
    public static void main(String[] args) throws Exception {
        Person person = new Person("Alice", 30, "likes pizza");

        // Создаём поток для записи в файл
        FileOutputStream fileOut = new FileOutputStream("person.bin");
        ObjectOutputStream out = new ObjectOutputStream(fileOut);

        // Сохраняем объект
        out.writeObject(person);

        // Закрываем потоки
        out.close();
        fileOut.close();

        System.out.println("Объект сериализован в файл person.bin");
    }
}

Шаг 3. Десериализация объекта из файла

import java.io.FileInputStream;
import java.io.ObjectInputStream;

public class DeserializeDemo {
    public static void main(String[] args) throws Exception {
        // Открываем поток для чтения из файла
        FileInputStream fileIn = new FileInputStream("person.bin");
        ObjectInputStream in = new ObjectInputStream(fileIn);

        // Восстанавливаем объект
        Person person = (Person) in.readObject();

        in.close();
        fileIn.close();

        System.out.println("Объект десериализован: " + person);
    }
}

Ожидаемый вывод

Объект сериализован в файл person.bin
Объект десериализован: Person{name='Alice', age=30, secret='null'}

Внимание! Поле transient не сериализуется. После десериализации оно будет равно null. Это важно для временных или чувствительных данных.

3. Ограничения и особенности

Все поля должны быть сериализуемыми

Если у класса есть поля, которые сами не реализуют Serializable (или содержат такие объекты), сериализация завершится с ошибкой. Например, поля типа Thread или Socket сделать «просто так» сериализуемыми нельзя.

Статические и transient-поля

  • Статические поля (static) не сериализуются: они принадлежат классу, а не конкретному объекту.
  • transient-поля — помеченные как transient явно исключаются из сериализации и после восстановления получают значения по умолчанию (null, 0 и т. п.).

Исключения

  • Попытка сериализовать объект, который не реализует Serializable, вызывает NotSerializableException.
  • При десериализации возможны ошибки: файл не найден, несовпадение классов, повреждённые данные и др.

Версии классов

Если вы измените структуру класса после сериализации (например, добавите/удалите поля), при десериализации может возникнуть InvalidClassException. Для контроля версий используется специальное поле serialVersionUID (о нём подробнее в одной из следующих лекций).

4. Практика: сериализация и десериализация объекта в файл

Допустим, у нас есть класс Person, и мы хотим сохранять список людей в файл и читать его обратно.

Класс Person (сериализуемый)

import java.io.Serializable;

public class Person implements Serializable {
    private String name;
    private int age;
    private transient String secret; // не будет сериализовано

    public Person(String name, int age, String secret) {
        this.name = name;
        this.age = age;
        this.secret = secret;
    }

    @Override
    public String toString() {
        return name + " (" + age + "), secret: " + secret;
    }
}

Сериализация списка людей

import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.List;

public class SerializeListDemo {
    public static void main(String[] args) throws Exception {
        List<Person> people = new ArrayList<>();
        people.add(new Person("Alice", 30, "likes pizza"));
        people.add(new Person("Bob", 25, "hates broccoli"));

        FileOutputStream fileOut = new FileOutputStream("people.bin");
        ObjectOutputStream out = new ObjectOutputStream(fileOut);

        // Сохраняем список
        out.writeObject(people);

        out.close();
        fileOut.close();

        System.out.println("Список людей сериализован.");
    }
}

Десериализация списка людей

import java.io.FileInputStream;
import java.io.ObjectInputStream;
import java.util.List;

public class DeserializeListDemo {
    public static void main(String[] args) throws Exception {
        FileInputStream fileIn = new FileInputStream("people.bin");
        ObjectInputStream in = new ObjectInputStream(fileIn);

        // Восстанавливаем список
        List<Person> people = (List<Person>) in.readObject();

        in.close();
        fileIn.close();

        for (Person p : people) {
            System.out.println(p);
        }
    }
}

Результат:

Alice (30), secret: null
Bob (25), secret: null

5. Типичные ошибки

Ошибка №1: Класс не реализует Serializable. Если забыть добавить implements Serializable, при попытке сериализации получите NotSerializableException. Это самая частая и простая ошибка.

Ошибка №2: Несериализуемое поле. Если объект содержит поле, которое не сериализуемо (например, Thread, Socket или любой другой тип без Serializable), сериализация «упадёт». Пометьте такие поля как transient или сделайте их сериализуемыми.

Ошибка №3: Изменение структуры класса. Если объект сериализован, а позже класс изменён (добавили/удалили поля), при чтении возможен InvalidClassException. Указывайте serialVersionUID для стабильного контроля версии.

Ошибка №4: Попытка сериализовать статические поля. Поля с модификатором static не сериализуются. После десериализации их значения будут такими, как в классе по умолчанию, а не теми, что были при сериализации.

Ошибка №5: Не закрыты потоки. Если не закрывать потоки после работы, можно получить повреждённый файл или утечку ресурсов. Используйте try-with-resources или явно закрывайте потоки.

Ошибка №6: Несовпадение классов. Если класс был переименован или перемещён в другой пакет, десериализация не сработает — требуется точное совпадение имени и пакета класса, сохранённого в потоке.

1
Задача
JAVA 25 SELF, 42 уровень, 3 лекция
Недоступна
Сериализуем простой объект
Сериализуем простой объект
1
Задача
JAVA 25 SELF, 42 уровень, 3 лекция
Недоступна
transient-поле и сериализация
transient-поле и сериализация
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ