пятница, 15 марта 2013 г.

Серилизация

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 необходимо вместо этих объектов создать и сохранить их копии.

Комментариев нет:

Отправить комментарий