Назад к вопросам
Middle+
69
questionbank

Как изменить стандартное поведение сериализации и десериализации в Java?

Sobes Copilot

Получайте ответы в реальном времени

Ответ от нейросети

sobes.tech AI

Для изменения стандартного поведения сериализации и десериализации используются методы writeObject и readObject, а также механизм Externalizable.

Использование writeObject и readObject

Класс, реализующий интерфейс Serializable, может объявлять эти методы. JVM вызовет их во время сериализации и десериализации соответственно.

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class MyCustomSerializable implements Serializable {

    private String data;
    private transient int sensitiveData; // это поле не будет сериализовано по умолчанию

    public MyCustomSerializable(String data, int sensitiveData) {
        this.data = data;
        this.sensitiveData = sensitiveData;
    }

    // Метод вызывается во время сериализации
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject(); // Сериализует не-transient поля по умолчанию
        out.writeInt(sensitiveData * 2); // Сериализуем модифицированное чувствительное поле
    }

    // Метод вызывается во время десериализации
    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject(); // Десериализует не-transient поля по умолчанию
        this.sensitiveData = in.readInt() / 2; // Десериализуем и восстанавливаем чувствительное поле
    }

    public String getData() {
        return data;
    }

    public int getSensitiveData() {
        return sensitiveData;
    }
}

Использование Externalizable

Интерфейс Externalizable предоставляет полный контроль над процессом сериализации и десериализации. Класс должен реализовать методы writeExternal и readExternal.

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class MyExternalizable implements Externalizable {

    private String publicData;
    private int privateData; // Все поля будут сериализованы/десериализованы вручную

    // Необходим конструктор без аргументов для Externalizable
    public MyExternalizable() {
        // Пустой конструктор
    }

    public MyExternalizable(String publicData, int privateData) {
        this.publicData = publicData;
        this.privateData = privateData;
    }

    // Метод вызывается во время сериализации
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(publicData); // Ручная сериализация publicData
        out.writeInt(privateData); // Ручная сериализация privateData
    }

    // Метод вызывается во время десериализации
    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        this.publicData = in.readUTF(); // Ручная десериализация publicData
        this.privateData = in.readInt(); // Ручная десериализация privateData
    }

    public String getPublicData() {
        return publicData;
    }

    public int getPrivateData() {
        return privateData;
    }
}

Сравнение подходов

Особенность Serializable + readObject/writeObject Externalizable
Контроль Частичный (дополнение к стандартному) Полный
Сложность реализации Проще для простых случаев Требует больше кода для всех полей
Производительность Обычно ниже Может быть выше за счет оптимизации
Совместимость версий Лучше поддерживается JVM Требует ручного управления изменениями полей
transient поля Игнорируются по умолчанию Должны быть сериализованы/десериализованы вручную
Конструктор Не требуется конструктор без аргументов (хотя по хорошей практике рекомендуется) Требует конструктор без аргументов