Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Десереализация в 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!
}
}
Как работает встроенная десереализация
- Проверка класса — JVM проверяет, что класс существует и имеет
serialVersionUID - Чтение данных — ObjectInputStream читает байты из источника
- Создание объекта — объект создаётся БЕЗ вызова конструктора через reflection
- Восстановление полей — значения полей восстанавливаются из байтов
- Вызов 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 для обмена данными между системами.