Главная » Java » Типы PutField и GetField в Java

0

 

   Схема сериализации/десериализации, предлагаемая по умолчанию, обычно работоспособна в большинстве ситуаций, хотя и не во всех. В частности, в процессе использования более изощренных классов и по мере их развития могут потребоваться средства доступа к исходным версиям структуры полей класса. Предположим, например, что в прежней версии класса для представления прямоугольника на плоскости использовалась информация о координатах его противоположных вершин, сохраняемая в виде значений четырех полей: xl, yl, х2 и у2. Если позже реализация класса подвергается изменению с целью обеспечения возможности представления прямоугольника с помощью координат одной из вершин и значений его ширины и высоты, набор полей может принять следующий вид: х, у, width и height. Если для сериализации полей объектов класса прежней версии применялась схема по умолчанию, мы столкнемся с проблемой совместимости: набор данных ранее сериализованных объектов не соответствует актуальному формату. Для решения проблемы следует предусмотреть средства преобразования данных одного формата в другой по мере обработки их методами readObject или writeObject. С этой целью применяются специальные типы сериализации структуры полей (serialized fields), позволяющие абстрагировать формат данных, подвергаемых сериализации, и получить доступ к отдельным полям:

 

public class Rectangle implements Serializable {

     private static final   long

serialVersionUID = -1307795172754062330L;

 

transient private double x,   y;

transient private double width,   height;

 

private void readobject(ObjectlnputStream in)

     throws lOException,   ClassNotFoundException

{

Objectlnputstream.GetField fields;

 fields = in. readFields() ;

 x = fields.get("xl",   0.0);

 у = fields.get("yl",   0.0);

double x2 = fields.get("x2",   0.0);

 double y2 = fields.get("y2",   0.0);

width = (x^2 – x);

 height =  (y^2  – у);

}

private void writeobject(ObjectOutputStream out)

throws  lOException

{

ObjectOutputStream.PutField fields  = out.putFields()

fields.put("xl",   x);

fields.put("yl".   У) ;

fields.put(x2",   x + width);

fields.put("y2",   у + height);

out.writeFieldsQ ;

}

private  static final  0bjectstreamField[]

 

serialPersistentFields = {

new ObjectstreamFieldC’xl", Double.TYPE),

new ObjectstreamFieldC’yl", Double.TYPE),

            new ObjectstreamField("x2", Double.TYPE),

new ObjectStreamField("y2", Double.TYPE),

    };

}

 

Прежде всего обратите внимание на строку определения поля serialVersionUID, служащего для обеспечения совместимости версий класса: изменение структуры полей класса при использовании схемы сериализации, принятой по умолчанию, с точки зрения механизма поддержки совместимости считается недопустимым, и поэтому мы обязаны объявить идентификатор версии в виде поля.

  Поля х, у, width и height обозначены модификатором transient, поскольку они не должны подвергаться сериализации, — в процессе сериализации содержимое полей нового формата должно быть преобразовано в соответствующие значения исходных полей, так как мы стремимся сохранить в потоке данные, отвечающие прежней структуре полей. В тексте метода writeObject создается объект типа ObjectOutputStream. PutField, позволяющий сохранить нужную структуру полей: поля х и у выступают в роли "старых" xl и yl, а значения х2 и у2 вычисляются, соответственно, на основе пар значений X, width и у, height. В каждом из вызовов метода put в качестве одного аргумента задается строка с наименованием поля, подлежащего сериализации, а второй аргумент содержит значение этого поля — тип значения обусловливает выбор одной из перегруженных форм put (существуют варианты put для каждого из простых типов, а также типа Object). Таким образом можно имитировать обычный процесс сериализации с сохранением требуемой структуры данных.

  При десериализации объекта Rectangle происходит процесс обратных преобразований. Метод readObject получает из потока объект типа ObjectlnputStream.GetField. Последний позволяет получить доступ к полям, подвергающимся десериализации, по имени. Существуют варианты метода get, возвращающие значения всех простых типов, а также ссылки типа Object. Каждому из методов get при вызове передается два аргумента: строка с наименованием поля и значение по умолчанию, которое должно быть возвращено, если Указанное поле не определено в текущей версии класса. Тип значения обусловливает выбор подходящей перегруженной версии get: например, указывая в качестве аргумента значение short, мы предполагаем, что должен быть вызван вариант метода get, возвращающий значения типа short. (Этот аспект заслуживает более пристального внимания. Как исполняющая система в процессе де-сеРиализации способна определить, к какому именно типу относятся значения, сохраненные средствами объекта PutField? Если не будут приняты соответствующие меры, то никак. Следует обозначить сериализуемые поля, объявив специальный массив объектов типа ObjectStreamField, что мы и сделали. Массив (нашем случае, seri al Persi stentFiel ds) должен быть объявлен точно так, так показано в тексте класса  Rectangle.  Каждый элемент массива является объектом класса ObjectStreamField, представляющим одно из полей,  подлещих сериализации. Объект конструируется на основе строки с наименованием поля и объекта класса Class, задающего тип поля. Теперь, используя информацию массива, механизм сериализации "знает", как представить данные о полях в потоке и каким образом интерпретировать их в процессе восстановления объек та.) В примере класса Rectangle все величины относятся к типу double; счи тываемые из потока значения полей xl и yl трактуются как координаты одной из вершин прямоугольника, х и у, а содержимое х2 и у2 используется для вы числения требуемых значений ширины, width, и высоты, height.

   Используя рассмотренный подход, класс Rectangle новой версии сможет осуществить десериализацию объектов старого формата, а вновь созданные объекты Rectangle смогут быть успешно десериализованы в контексте класса прежней версии — если предположить, что все виртуальные машины применяют совместимые версии протокола потоковой сериализации. Такой протокол определяет правила фактической компоновки сериализованных объектов в потоке независимо от того, использовались ли схема сериализации, предлагаемая по умолчанию, либо механизм сериализации структуры полей. Это значит, что форма представления данных об объекте в потоке и результат их последующей десериализации не зависят ни от порядка вызова методов put, ни от того, известна ли программе "правильная" последовательность обращения к методам get, — вы вправе с помощью put и get адресовать поля в любом порядке и любое количество раз.

 

Источник: Арнолд, Кен, Гослинг, Джеймс, Холмс, Дэвид. Язык программирования Java. 3-е изд .. : Пер. с англ. – М. : Издательский дом «Вильяме», 2001. – 624 с. : ил. – Парал. тит. англ.

По теме:

  • Комментарии