public interface Serializable {
}
Метим интерфейсом Serializable (а посути маркеторм, потому что этот интерфейс пустой). После этого мы можем пихать такой обьект в ObjectOutputStream. С помощью рефлексии обьект запихается в поток (серилизуется) . Процесс десерилизации -- следующий: под обьект выделяется память, и в его поля загружаются значения из потока, никакие контруктора не вызываются при этом. Если же класс наследует родителя у которого нет интрефейса Serializable, то вызывается конструктор родителя без аргументов, поэтому если у родетеля не будет такого контруктора дисерилизация упадет. Возможно несовпадение верисии класса, если мы изменили класс и пытаемся его десерилизировать из сохраненной старой версии, то произойдет ексепшин:java.io.InvalidClassException: test.ser2.ChildExt;
local class incompatible: stream classdesc serialVersionUID = 8218484765288926197,
local class serialVersionUID = 1465687698753363969
Это потому что в обьект компилятор добавляет поле private static final long serialVersionUID, которое исчисляется на основании полей методов и их порядка в классе, мы можем обмануть компилятор если уже определим это значение, и тогда если получится, то сторая версия обьекта дисерилизуется в новую версию класса, но это крайне не рекомендуется, потому что есть большая вероятность, что всеравно будут варианты, когда десерилизация упадет.
Если мы хотим переписать алгоритм серилизации с целью оптимизации, то для этого лучше использовать интерфейс Externalizable
public interface Externalizable extends java.io.Serializable {
void writeExternal(ObjectOutput out) throws IOException;
void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
Этот интерфейс явно для этого, и компилятор проверяет, если класс имплементирует оба интерфейса, то именно этот имеет преимущество и страндартная серилизация/де не происходит.Но также возможно немного поколдовать, по сути компилятор для классов помеченных Serializable, добавляет два метода:
private void writeObject(ObjectOutputStream out) throws IOException; private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException;В которых он генерит логику серилизации на основе своих алгоритмов, поэтому если мы в Serializable реализуем два таких метода, то они сгенерированы не будут. Но это крайне не рекомендуется, потому что для этого существует Externalizable, а также алгоритм там действительно сложный и он позволяет сохранить обьекты и цикличным взаимоссыланием, и несколько обьектов, которые посути ссылки на один в памяти, тоже имеено так восстановится.
Но есть реальное применение этим методам: когда мы хотим сделать класс не серилизуемым, когда у него родитель таков - для этого:
private void writeObject(ObjectOutputStream out) throws IOException
{
throw new NotSerializableException("Не сегодня!");
}
private void readObject(ObjectInputStream in) throws IOException
{
throw new NotSerializableException("Не сегодня!");
}
Правила безопасности:
Правило 1. После десериализации объекта необходимо проверить его внутреннее состояние (инварианты) на правильность, точно так же, как и при создании с помощью конструктора. Если объект не прошел такую проверку, необходимо инициировать исключение
java.io.InvalidObjectException.Правило 2. Если в составе класса
A присутствуют объекты, которые не должны быть доступными для изменения извне, то при десериализации экземпляра класса A необходимо вместо этих объектов создать и сохранить их копии.
Комментариев нет:
Отправить комментарий