Обнаружить Тайны Явы TM Преобразование в последовательную форму API

Todd Greanier; Переизданный от JavaWorld
Июль 2000

Все мы знаем Яву TM Платформа позволяет нам создавать объекты(цели) многократного использования в памяти. Однако, все те объекты(цели) существуют только пока Ява TM Действительная(виртуальная) машина(механизм) 1 Остается бежать. Было бы хорошо, если объекты(цели), которые мы создаем, могли бы существовать вне срока службы(продолжительности жизни) действительной(виртуальной) машины(механизма), не так ли? Хорошо, с преобразованием в последовательную форму объекта(цели), Вы можете сглаживать ваши объекты(цели) и повторное использование их мощными способами.

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

Повсюду этой статьи(изделия), мы исследуем, как упорствовать(сохраниться) ваши объекты(цели) Явы, начинающиеся с основ и переходящий к более продвинутым концепциям. Мы изучим три различных способа исполнить преобразование в последовательную форму - использование протокола неплатежа, настройка протокола неплатежа, и создания нашего собственного протокола - и мы исследуем предприятия(беспокойства), которые возникают с любой схемой постоянства типа объекта(цели) caching, контроля(управления) версии, и проблем(выпусков) выполнения(работы).

В соответствии с заключением этой статьи(изделия), Вы должны иметь твердое понимание этого мощным все же иногда плохо понятой Явой API.

 

Первые Вещи Сначала: Механизм Неплатежа

Давайте начало с основами. Чтобы упорствовать(сохраниться) объект(цель) в Яве, мы должны иметь постоянный объект(цель). Объект(цель) отмечен serializable, осуществляя java.io. Serializable интерфейс, который показывает к основному API, что объект(цель) может быть сглажен в байты и впоследствии раздут в будущем.

Давайте смотреть на постоянный класс, который мы будем использовать, чтобы демонстрировать механизм преобразования в последовательную форму:

10 импорта java.io. Serializable;
20 импорта java.util. Дата;
30 импорта java.util. Календарь;
40 общественных классов PersistentTime осуществляет Serializable
50 {
60 частных времен Даты;
70
80 публики PersistentTime ()
90 {
100      раз = Calendar.getInstance () .getTime ();
110   }
120
130    общественных Дат getTime ()
140    {
150 времен возвращения;
160   }
170 }
 

Поскольку Вы можете видеть, единственная вещь мы были должны делать по-другому от создания нормального класса - орудие java.io. Serializable соединяют на линии 40. Полностью пустой Serializable - только интерфейс маркера - это просто позволяет механизму преобразования в последовательную форму проверять, что класс мочь упорствовать(сохраняться). Таким образом, мы поворачиваемся к первому правилу(правлению) преобразования в последовательную форму:

Правило(правление) *1: объект(цель), который нужно упорствовать(сохранить) должен осуществить Serializable, соединяют или унаследуют то выполнение от его иерархии объекта(цели).

Следующий шаг, чтобы фактически упорствовать(сохраниться) объект(цель). Это сделано с java.io. ObjectOutputStream класс. Тот класс - поток фильтра - это обернуто вокруг потока байта низшего уровня (называемый потоком узла) чтобы обратиться с протоколом преобразования в последовательную форму для нас. Потоки Узла могут использоваться, чтобы писать системам файла или даже поперек гнезд. Это означает, что мы могли легко переходить, сглаженный объект(цель) поперек сети телеграфирует и восстановился это быть с другой стороны!

Смотреть на кодекс, имел обыкновение экономить(спасать) объект(цель) PersistentTime:

10 импорта java.io. ObjectOutputStream;
20 импорта java.io. FileOutputStream;
30 импорта java.io. IOException;
40 общественных классов FlattenTime
50 {
60 публики статическая пустота главный (Вереница(нить) [] args)
70 {
80 Верениц(нитей) filename = "time.ser";
90, если (args.length > 0)
100     {
110       filename = args [0];
120    }
130     PersistentTime время = новый PersistentTime ();
140     FileOutputStream fos = пустой указатель;
150     ObjectOutputStream из = пустой указатель;
160     попыток
170     {
180       fos = новый FileOutputStream (filename);
190       из = новый ObjectOutputStream (fos);
200       out.writeObject (время);
210       out.close ();
220    }
230     ловят(поймают) (IOException исключая)
240     {
250       ex.printStackTrace ();
260    }
270  }
280}
 

Реальная работа заскочит линия 200, когда мы называем ObjectOutputStream.writeObject () методом, который начинает механизм преобразования в последовательную форму, и объект(цель) сглажен (в том случае к файлу).

Чтобы восстановить файл, мы можем использовать следующий кодекс:

10 импорта java.io. ObjectInputStream;
20 импорта java.io. FileInputStream;
30 импорта java.io. IOException;
40 импорта java.util. Календарь;
50 общественных классов InflateTime
60 {
70 публики статическая пустота главный (Вереница(нить) [] args)
80 {
90 Верениц(нитей) filename = "time.ser";
100    , если (args.length > 0)
110     {
120       filename = args [0];
130    }
140   PersistentTime время = пустой указатель;
150   FileInputStream fis = пустой указатель;
160   ObjectInputStream в = пустой указатель;
170   попыток
180   {
190     fis = новый FileInputStream (filename);
200     в = новый ObjectInputStream (fis);
210     раз = (PersistentTime) in.readObject ();
220     in.close ();
230  }
240   ловят(поймают) (IOException исключая)
250   {
260     ex.printStackTrace ();
270  }
280   ловят(поймают) (ClassNotFoundException исключая)
290   {
300     ex.printStackTrace ();
310  }
320   // распечатывают восстановленное время
330   System.out.println (" Сглаженное время: " + time.getTime ());
340   System.out.println ();
350      // распечатывают текущее время
360   System.out.println (" Текущее время: " + Calendar.getInstance () .getTime ());
370}
380}
 

В кодексе выше, восстановление объекта(цели) происходит на линии 210 с ObjectInputStream.readObject () запрос метода. Запрос метода читает в сырых байтах, что мы предварительно упорствовали(сохранились) и создаем живой объект(цель), который является точной точной копией оригинала. Поскольку readObject () может читать любой объект(цель) serializable, бросок к правильному типу требуется. С этим в памяти, файл класса должен быть доступен от системы, в которой восстановление происходит. Другими словами, файл класса объекта(цели) и методы не сэкономлены(спасены); только государство(состояние) объекта(цели) сэкономлено(спасено).

Позже, на линии 360, мы просто называем getTime () методом восстановить время, которое первоначальный объект(цель) сглаживал. Сглаживающееся время сравнено с текущим временем, чтобы демонстрировать, что механизм действительно работал как ожидается.

 

Nonserializable Объекты(цели)

Основной механизм преобразования в последовательную форму Явы прост использовать, но есть несколько больше вещи знать. Как упомянуто прежде, только возражает отмеченный Serializable, можно упорствовать(сохраняться). Java.lang. Класс Объекта(цели) не осуществляет тот интерфейс. Поэтому, не все объекты(цели) в Яве можно упорствовать(сохраняться) автоматически. Хорошие новости - то большинство их - подобно AWT и Колебанию GUI компоненты, вереницы(нити), и множества - являются serializable.

С другой стороны, некоторые классы(занятия) уровня системы типа Нити, OutputStream и ее подклассов(подзанятий), и Гнезда - не serializable. Действительно, это не делало бы любой смысл, если они были. Например, нить, бегущая в моем JVM использовала бы память моей системы. Упорство это и при попытке управлять этим в вашем JVM не имело бы смысла вообще. Другой важный пункт(точка) о java.lang. Объект(цель), не осуществляющий интерфейс Serializable состоит в том, что любой класс, который Вы создаете, который расширяет(продлевает) только Объект(цель) (и никакие другие классы(занятия) serializable) - не serializable, если Вы не осуществляете интерфейс самостоятельно (как сделано с предыдущим примером).

Та ситуация представляет проблему: что, если мы имеем класс, который содержит случай Нити? В том случае, мы когда-либо можем упорствовать(сохраниться) объекты(цели) того типа? Ответ - да, пока мы сообщаем механизму преобразования в последовательную форму наши намерения, отмечая объект(цель) Нити нашего класса как переходный процесс.

Давайте предполагать, что мы хотим создать класс, который исполняет мультипликацию. Я фактически не буду обеспечивать кодекс мультипликации здесь, но вот - класс, который мы будем использовать:

10 импорта java.io. Serializable;
20 общественных классов PersistentAnimation осуществляет Serializable, Runnable
30 {
40 переходных частных Нитей animator;
50 частного int animationSpeed;
60 публики PersistentAnimation (int animationSpeed)
70 {
80 this.animationSpeed = animationSpeed;
90 animator = новая Нить (это);
100     animator.start ();
110  }
120       общественных управляемых пустот ()
130   {
140    , в то время как (истинный)
150     {
160       // делают мультипликацию здесь
170    }
180  }
190}
 

Когда мы создаем случай PersistentAnimation класса, нить animator будет создана и начат, поскольку мы ожидаем. Мы отметили нить на линии 40 переходных процессов, чтобы сообщить механизму преобразования в последовательную форму, что область(поле) не должна быть сэкономлена(спасена) наряду с остальной частью что государство(состояние) объекта(цели) (в том случае, полевая скорость). Суть: Вы должны отметить переходный процесс любая область(поле), которая или(любой) не может быть преобразована в последовательную форму или любая область(поле), которую Вы не хотите преобразованный в последовательную форму. Преобразование в последовательную форму не заботится о модификаторах доступа типа частного - все непереходные области(поля) рассматриваются частью постоянного государства(состояния) объекта(цели) и имеют право на постоянство.

Поэтому, мы имеем другое правило(правление) добавить. Вот - оба правила относительно постоянных объектов(целей):

 

 

Настроить Протокол Неплатежа

Давайте ход на к второму способу исполнить преобразование в последовательную форму: настройте протокол неплатежа. Хотя кодекс мультипликации выше демонстрирует, как нить могла быть включена как часть объекта(цели) при все еще создании того объекта(цели) быть serializable, есть главная проблема с этим, если мы вспоминаем, как Ява создает объекты(цели). К остроумию, когда мы создаем, объект(цель) с новым keyword, конструктор объекта(цели) называется только, когда новый случай класса создан. При хранении, что основной факт в памяти, давайте повторно посещать наш кодекс мультипликации. Сначала, мы instantiate объект(цель) типа PersistentAnimation, который начинает последовательность нити мультипликации. Затем, мы преобразовываем в последовательную форму объект(цель) с тем кодексом:

PersistentAnimation мультипликация = новый PersistentAnimation (10);
FileOutputStream fos = ...
ObjectOutputStream из = новый ObjectOutputStream (fos);
Out.writeObject (мультипликация);
 

Все кажется прекрасным, пока мы не читаем объект(цель) назад в с запросом к readObject () метод. Помните, конструктор называется только, когда новый случай создан. Мы не создаем новый случай здесь, мы восстанавливаем упорствованный(сохраненный) объект(цель). Результат конца - объект(цель) мультипликации, будет работать только однажды, когда это - первый instantiated. Вид делает бесполезным упорствовать(сохраниться) это, huh?

Хорошо, есть хорошие новости. Мы можем делать нашу работу объекта(цели), путем мы хотим это к; мы можем делать переначало мультипликации после восстановления объекта(цели). Чтобы выполнять это, мы могли, например, создавать startAnimation () метод помощника, который делает то, что конструктор в настоящее время делает. Мы могли тогда вызывать(называть) тот метод от конструктора, после в котором мы читаем объекту(цели) назад. Не плохо, но это представляет большее количество сложности. Теперь, любой, кто хочет использовать тот объект(цель) мультипликации, будет должен знать, что метод должен назваться после нормального процесса deserialization. Это не делает для механизма без шва, кое-что, что Преобразование в последовательную форму Явы API обещает разработчикам.

Есть, однако, странный все же лукавое решение. Используя встроенную особенность механизма преобразования в последовательную форму, разработчики могут увеличивать нормальный процесс, обеспечивая два метода внутри их файлы класса. Те методы:

 

Заметить, что оба методы - (и должны быть) объявлял частный, доказывая, что ни один(никакой) метод не унаследован и отвергнут или перегруженный. Уловка здесь - то, что действительная(виртуальная) машина(механизм) автоматически проверит, чтобы видеть, объявлен ли любой метод в течение соответствующего запроса метода. Действительная(виртуальная) машина(механизм) может вызывать(называть) частные методы вашего класса всякий раз, когда требуется, но никакие другие объекты(цели) не могут. Таким образом, целостность(честность) класса обслужена(поддержана), и протокол преобразования в последовательную форму может продолжать работать как нормальный. Протокол преобразования в последовательную форму всегда используется тот же самый путь, звоня любой ObjectOutputStream.writeObject () или ObjectInputStream.readObject (). Так, даже при том, что те специализировались, частные методы обеспечиваются, работы преобразования в последовательную форму объекта(цели) тот же самый путь, насколько любой объект(цель) запроса заинтересован(обеспокоен).

Рассмотрение все, что, давайте смотреть на пересмотренную версию PersistentAnimation, который включает те частные методы позволить нам иметь контроль над процессом deserialization, давая нам псевдоконструктор:

10 импорта java.io. Serializable;
20 общественных классов PersistentAnimation осуществляет Serializable, Runnable
30 {
40 переходных частных Нитей animator;
50 частного int animationSpeed;
60 публики PersistentAnimation (int animationSpeed)
70 {
80 this.animationSpeed = animationSpeed;
90 startAnimation ();
100  }
110       общественных управляемых пустот ()
120   {
130    , в то время как (истинный)
140     {
150       // делают мультипликацию здесь
160    }
170  }
180   частных пустот writeObject (ObjectOutputStream из) бросает IOEXCEPTION
190   {
200     out.defaultWriteObject ();
220  }
230   частных пустот readObject (ObjectInputStream в) бросает IOEXCEPTION, ClassNotFoundException
240   {
250     // наш "псевдоконструктор"
260     in.defaultReadObject ();
270     // теперь мы - "живой" объект(цель) снова, так что давайте бежать, восстанавливают и начало
280     startAnimation ();
290
300  }
310   частных пустот startAnimation ()
320   {
330     animator = новая Нить (это);
340     animator.start ();
350  }
360}
 

Заметить первую линию каждого из новых частных методов. Те запросы делают то, чем они кажутся подобно - они исполняют письмо неплатежа и чтение сглаженного объекта(цели), который является важным, потому что мы не заменяем нормальный процесс, мы только добавляем к этому. Те методы работают, потому что запрос к ObjectOutputStream.writeObject () начинает протокол преобразования в последовательную форму. Сначала, объект(цель) проверен, чтобы гарантировать, что это осуществляет Serializable, и тогда это проверено, чтобы видеть, обеспечиваются ли или(любой) из тех частных методов. Если им обеспечивают, класс потока пропускают как параметр, давая кодекс контроль над его использованием.

Те частные методы могут использоваться для любой настройки, которую Вы должны делать к процессу преобразования в последовательную форму. Шифрование могло быть добавлено к продукции(выпуску) и decryption к входу (обратите внимание, что байты написаны и читают в cleartext без путаницы вообще). Они могли использоваться, чтобы добавить дополнительные данные к потоку, возможно компания versioning кодекс. Возможности верно безграничны.

 

Остановить То Преобразование в последовательную форму!

ХОРОШО, мы видели весьма немного о процессе преобразования в последовательную форму, теперь давайте видеть несколько больше. Что, если Вы создаете класс, чей суперкласс - serializable, но Вы не хотите, чтобы тот новый класс был serializable? Вы не можете неосуществлять интерфейс, так если ваш суперкласс осуществляет Serializable, ваш новый класс осуществляет это, также (принятие(предположение) обоих правил, внесенных в список выше встречены(выполнены)). Чтобы останавливать автоматическое преобразование в последовательную форму, Вы можете еще раз использовать частные методы только бросить NotSerializableException. Есть, как это было бы сделано:

10 частных пустот writeObject (ObjectOutputStream из) бросает IOEXCEPTION
20 {
30 бросают новый NotSerializableException (" Не сегодня! ");
40}
50 частных пустот readObject (ObjectInputStream в) бросает IOEXCEPTION
60 {
70 бросают новый NotSerializableException (" Не сегодня! ");
80}
 

Любая попытка писать или читать тот объект(цель) будет теперь всегда кончаться бросаемым исключением. Помните, так как те методы объявлены частными, никто не мог изменять ваш кодекс без исходного кодекса, доступного им - никакое отвергание тех методов не будет позволяться Явой.

 

Создать Ваш Собственный Протокол: Интерфейс Externalizable

Наше обсуждение было бы неполно, чтобы не упомянуть третий выбор для преобразования в последовательную форму: создайте ваш собственный протокол с интерфейсом Externalizable. Вместо осуществления интерфейса Serializable, Вы можете осуществлять Externalizable, который содержит два метода:

 

Только отвергните те методы обеспечить ваш собственный протокол. В отличие от предыдущих двух изменений(разновидностей) преобразования в последовательную форму, ничто не обеспечивается для свободного здесь, хотя. То есть протокол - полностью в ваших руках. Хотя это - более трудный сценарий, это также наиболее управляемо. Ситуация примера для того дополнительного типа преобразования в последовательную форму: читайте и пишите PDF файлы с заявлением(применением) Явы. Если Вы знаете, как писать и читать PDF (последовательность требуемых байтов), Вы могли обеспечивать PDF-ОПРЕДЕЛЕННЫЙ протокол в writeExternal и readExternal методах.

Также, как прежде, тем не менее, нет никакого различия в том, как, класс который осуществляет Externalizable, используется. Только вызовите(назовите) writeObject () или readObject и, voila, те externalizable методы будут называться автоматически.

 

Gotchas

Есть несколько вещей о протоколе преобразования в последовательную форму, который может казаться очень странным для разработчиков, кто не знают. Конечно, это - цель статьи(изделия) - чтобы получить Вас знающий! Так что давайте обсуждать несколько из тех gotchas и видеть, можем ли мы понимать, почему они существуют и как обратиться с ними.

 

Caching Объекты(цели) в Потоке

Сначала, рассмотрите ситуацию, в которой объект(цель) написан потоку и тогда написан снова позже. По умолчанию, ObjectOutputStream поддержит(обслужит) ссылку(рекомендацию) на объект(цель), письменный этому. Это означает, что, если государство(состояние) письменного объекта(цели) написано и тогда написано снова, новое государство(состояние) не будет сэкономлено(спасено)! Вот - отрывок кодекса, который показывает что проблему в действии:

10 ObjectOutputStream из = новый ObjectOutputStream (...);
20 MyObject obj = новый MyObject (); // должен быть Serializable
30 obj.setState (100);
40 out.writeObject (obj); // экономит(спасает) объект(цель) с государством(состоянием) = 100
50 obj.setState (200);
60 out.writeObject (obj); // не экономит(спасает) новое государство(состояние) объекта(цели)
 

Есть два способа управлять той ситуацией. Сначала, Вы могли удостовериться, чтобы всегда закрыть поток после того, как пишущийся запрос, гарантируя новый объект(цель) выписан каждый раз. Во вторых, Вы могли называть ObjectOutputStream.reset () методом, который сообщит потоку выпускать тайник ссылок(рекомендаций), это проводит(держит) так весь новым, пишут, что запросы будут фактически написаны. Будьте осторожны, хотя - перенабор смывает полный тайник объекта(цели), так все объекты(цели), что было написано, мог быть переписан.

 

Контроль(управление) Версии

С нашей секундой gotcha, вообразите, что Вы создаете класс, instantiate это, и выписываете это к потоку объекта(цели). Тот сглаженный объект(цель) сидит в системе файла в течение некоторого времени. Тем временем, Вы модернизируете файл класса, возможно добавляя новую область(поле). Что случается, когда Вы пробуете читать в сглаженном объекте(цели)?

Хорошо, плохие новости - то, что исключение будет брошено - определенно, java.io. InvalidClassException - потому что всем постоянно-способным классам(занятиям) автоматически дают уникальный идентификатор. Если идентификатор класса не равняется идентификатору сглаженного объекта(цели), исключение будет брошено. Однако, если Вы действительно думаете об этом, почему это должно быть брошено только, потому что я добавлял область(поле)? Разве область(поле) только не могла быть установлена на ее ценность неплатежа и тогда выписанный следующий раз?

Да, но требуется небольшая манипуляция кодекса. Идентификатор, который является частью всех классов(занятий), обслужен(поддержан) в области(поле), называемой serialVersionUID. Если Вы желаете управлять versioning, Вы просто должны обеспечить serialVersionUID область(поле) вручную и гарантировать, что это всегда то же само, независимо от того какой изменения(замены) Вы делаете к classfile. Вы можете использовать полезность, которая идет с JDK распределением, называемым serialver, чтобы видеть то, что тот кодекс был бы по умолчанию (это - только кодекс мешанины объекта(цели) по умолчанию).

Вот - пример использования serialver с классом по имени Баз:

> serialver Баз
> Баз: статический финал долго serialVersionUID = 10275539472837495L;
 

Просто копируйте возвращенную линию с версией ID, и приклейте это в ваш кодекс. (На коробке Окон, Вы можете управлять той полезностью с - выбор показа, чтобы упростить процедуру пасты и копию.) Теперь, если Вы делаете, любые изменения(замены) к файлу класса База, только гарантируют, что та же самая версия ID определена, и все будут хорошо.

Работы контроля(управления) версии большой пока изменения(замены) совместимы. Совместимые изменения(замены) включают добавление или удаление метода или области(поля). Несовместимые изменения(замены) включают изменение(замену) иерархии объекта(цели) или удаления выполнения интерфейса Serializable. Полный список совместимых и несовместимых изменений(замен) сдает(сданный) Спецификация Преобразования в последовательную форму Явы.

 

Соображения(рассмотрения) Выполнения(работы)

Наша треть gotcha: механизм неплатежа, хотя просто, чтобы использовать, не лучший исполнитель. Я выписал объект(цель) Даты к файлу 1,000 раза, повторяя те времена процедуры 100. Среднее время, чтобы выписать объект(цель) Даты было 115 миллисекунд. Я тогда вручную выписал объект(цель) Даты, используя стандартный ввод - вывод тот же самый номер(число) повторений; среднее время было 52 миллисекунды. Почти половина времени! Есть часто обмен между удобством и выполнением(работой), и преобразование в последовательную форму не доказывает никакой различным. Если скорость - первичное соображение(рассмотрение) для вашего заявления(применения), Вы можете хотеть рассмотреть здание таможенного протокола.

Другое соображение(рассмотрение) касается вышеупомянутого факта, которые возражают, что ссылки(рекомендации) - cached в потоке продукции(выпуска). Из-за этого, система не может мусор собирать(забирать) объекты(цели), письменные потоку, если поток не закрыт. Лучший ход, как всегда со вводом - выводом, должен закрыть потоки как можно скорее, после пишущихся действий.

 

Заключение

Преобразование в последовательную форму в Яве просто подстрекать и почти как простой осуществить. Понимание трех различных путей осуществления преобразования в последовательную форму должно помочь в изгибе API к вашему желанию. Мы видели много механизм преобразования в последовательную форму в той статье(изделии), и я надеюсь, что это сделало вещи более ясными и не худшими. Суть, как со всем кодированием, должна поддержать(обслужить) здравый смысл в пределах границ API дружественных отношений. Та статья(изделие) разместила сильное основание понимания Преобразования в последовательную форму Явы API, но я рекомендую просмотреть спецификацию, чтобы обнаружить более мелкозернистые детали.

Coffecup Эмблема

Переизданный с разрешением от издания Июня 2000 JavaWorld журнала. Copyright ITWORLD.COM, Inc, IDG компания Коммуникаций. Регистр для передовой статьи e-mailalerts

 

Об Авторе

 

Тодд Греаниер, директор технологии для Обучения ComTech, преподал и развивал Яву, так как это было представлено публично. Эксперт в распределенных технологиях Явы, он преподает классы(занятия) в широком диапазоне тем, включая JDBC TM, RMI, CORBA, UML, Колебание, servlets/JSP TM, безопасность, JavaBeans TM, Предприятие Бобы Явы TM, и мультипронизывание. Он также создает таможенные семинары для корпораций, наклонных к их определенным потребностям. Todd живет в провинциальных областях штата Нью-Йорк с его женой, Стасей, и его котом, Бобом.