← Назад к вопросам

Что такое десереализация?

1.6 Junior🔥 191 комментариев
#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Десереализация в Java

Десереализация — это процесс преобразования потока байтов обратно в объект, восстанавливая его состояние. Это обратный процесс от сереализации, которая преобразует объект в последовательность байтов.

Десереализация позволяет:

  • Восстанавливать объекты из файлов
  • Получать объекты из сетевых соединений
  • Восстанавливать состояние приложения
  • Кешировать объекты

Сереализация vs Десереализация

Сереализация:   Объект → Байты → Хранилище/Сеть
                 ↓
Десереализация: Байты ← Восстановление ← Хранилище/Сеть

Встроенная сереализация Java (java.io.Serializable)

import java.io.*;

// 1. Класс должен реализовать Serializable интерфейс
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private String name;
    private int age;
    private transient String password; // Не будет сереализовано
    
    public User(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }
    
    // Getters
    public String getName() { return name; }
    public int getAge() { return age; }
}

// Сереализация: объект → файл
public class SerializationExample {
    public static void main(String[] args) throws Exception {
        // Создаём объект
        User user = new User("Alice", 30, "secret123");
        
        // Сереализуем в файл
        try (FileOutputStream fos = new FileOutputStream("user.ser");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            oos.writeObject(user);
            System.out.println("Объект сереализован в user.ser");
        }
    }
}

// Десереализация: файл → объект
public class DeserializationExample {
    public static void main(String[] args) throws Exception {
        User deserializedUser = null;
        
        // Десереализуем из файла
        try (FileInputStream fis = new FileInputStream("user.ser");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            deserializedUser = (User) ois.readObject();
            System.out.println("Объект десереализован из user.ser");
        }
        
        // Используем восстановленный объект
        System.out.println("Имя: " + deserializedUser.getName());
        System.out.println("Возраст: " + deserializedUser.getAge());
        System.out.println("Пароль: " + deserializedUser.getPassword()); // null!
    }
}

Как работает встроенная десереализация

  1. Проверка класса — JVM проверяет, что класс существует и имеет serialVersionUID
  2. Чтение данных — ObjectInputStream читает байты из источника
  3. Создание объекта — объект создаётся БЕЗ вызова конструктора через reflection
  4. Восстановление полей — значения полей восстанавливаются из байтов
  5. Вызов readObject() — если определен, вызывается custom логика восстановления

serialVersionUID

public class Product implements Serializable {
    private static final long serialVersionUID = 2L; // Версия класса
    
    private String name;
    private double price;
}

Зачем нужен?

  • Если класс изменился, а serialVersionUID прежний → JVM выбросит InvalidClassException
  • Позволяет контролировать совместимость старых и новых версий
  • Автоматически вычисляется, если не указан (небезопасно)
// При десереализации несовместимого класса
try {
    ois.readObject();
} catch (InvalidClassException e) {
    System.out.println("Несовместимая версия класса");
}

JSON десереализация (Jackson, Gson)

В реальных приложениях чаще используют JSON вместо встроенной сереализации.

import com.fasterxml.jackson.databind.ObjectMapper;

public class JsonDeserialization {
    
    static class User {
        public String name;
        public int age;
        public User() {} // Нужен default конструктор
    }
    
    public static void main(String[] args) throws Exception {
        String json = "{\"name\":\"Bob\",\"age\":25}";
        
        ObjectMapper mapper = new ObjectMapper();
        
        // Десереализация JSON → объект
        User user = mapper.readValue(json, User.class);
        
        System.out.println("Имя: " + user.name);
        System.out.println("Возраст: " + user.age);
    }
}

Пользовательская десереализация

public class Account implements Serializable {
    private String accountId;
    private double balance;
    private LocalDateTime createdAt;
    
    // Custom десереализация
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        // Читаем стандартные поля
        ois.defaultReadObject();
        
        // Custom логика
        System.out.println("Account десереализован: " + accountId);
        
        // Валидация
        if (balance < 0) {
            throw new InvalidObjectException("Баланс не может быть отрицательным");
        }
    }
    
    // Для версионирования при эволюции класса
    private Object readResolve() throws ObjectStreamException {
        // Трансформация после десереализации
        return this;
    }
}

Проблема безопасности при десереализации

Уязвимость: Если десереализовать недоверенные данные, может выполниться вредоносный код!

// ОПАСНО! Не десереализуй данные с интернета!
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object obj = ois.readObject(); // Может вызвать вредоносный код

Решение:

// 1. Использовать JSON вместо встроенной сереализации
ObjectMapper mapper = new ObjectMapper();
User user = mapper.readValue(jsonString, User.class);

// 2. Использовать ObjectInputFilter (Java 9+)
object-input-filter = "com.example.MyClass;!*"

// 3. Custom ClassLoader
class SafeObjectInputStream extends ObjectInputStream {
    @Override
    protected Class<?> resolveClass(ObjectStreamClass osc) {
        if (!isAllowed(osc.getName())) {
            throw new InvalidClassException("Запрещённый класс: " + osc.getName());
        }
        return super.resolveClass(osc);
    }
    
    private boolean isAllowed(String className) {
        // Белый список разрешённых классов
        return className.startsWith("com.example");
    }
}

Сравнение методов сереализации

МетодБезопасностьСкоростьРазмерИспользование
java.io.SerializableНизкаяБыстроБольшойLegacy код
JSON (Jackson/Gson)ВысокаяМедленнееМеньшеREST API, Storage
Protocol BuffersВысокаяОчень быстроМинимумgRPC, микросервисы
KryoСредняяОчень быстроМинимумPerformance критично

Практический пример: Кеш с десереализацией

public class SerializableCache {
    
    public static void saveCache(Map<String, User> cache, String filename) {
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream(filename))) {
            oos.writeObject(cache);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @SuppressWarnings("unchecked")
    public static Map<String, User> loadCache(String filename) {
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream(filename))) {
            return (Map<String, User>) ois.readObject();
        } catch (IOException | ClassNotFoundException e) {
            return new HashMap<>(); // Вернуть пустой кеш
        }
    }
}

Когда использовать?

Используй Java сереализацию:

  • Кеширование объектов на диск
  • RMI (Remote Method Invocation)
  • Сохранение состояния приложения
  • Только для내部использования (не для API)

Избегай Java сереализации:

  • Передача данных через интернет (используй JSON)
  • Взаимодействие с другими языками (используй JSON/Protocol Buffers)
  • Ненадёжные источники данных (security risk)

Заключение

Десереализация — это мощный механизм восстановления объектов из потока байтов. Однако встроенная Java сереализация имеет серьёзные проблемы безопасности. В modern приложениях рекомендуется использовать JSON или Protocol Buffers для обмена данными между системами.