1. введение
В этом уроке мы быстро рассмотрим интерфейс java java.io.Externalizable interface . Основная цель этого интерфейса-облегчить пользовательскую сериализацию и десериализацию.
Прежде чем мы продолжим, убедитесь, что вы проверили сериализацию в статье Java. Следующая глава посвящена сериализации объекта Java с помощью этого интерфейса.
После этого мы обсудим ключевые различия по сравнению с интерфейсом java.io.Serializable .
2. Внешний Интерфейс
Externalizable расширяется из интерфейса java.io.Serializable marker. Любой класс, реализующий интерфейс Externalizable , должен переопределять методы writeExternal () , readExternal () . Таким образом, мы можем изменить поведение сериализации JVM по умолчанию.
2.1. Сериализация
Давайте рассмотрим этот простой пример:
public class Country implements Externalizable { private static final long serialVersionUID = 1L; private String name; private int code; // getters, setters @Override public void writeExternal(ObjectOutput out) throws IOException { out.writeUTF(name); out.writeInt(code); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { this.name = in.readUTF(); this.code = in.readInt(); } }
Здесь мы определили класс Country , который реализует интерфейс Externalizable и реализует два упомянутых выше метода.
В методе writeExternal() мы добавляем свойства объекта в поток ObjectOutput . Это имеет стандартные методы, такие как writeUTF() для String и writeInt() для значений int.
Далее, для десериализации объекта мы читаем из Object Input stream , используя методы readout(), gradient() для чтения свойств в том же точном порядке, в котором они были записаны.
Рекомендуется добавить serialVersionUID вручную. Если он отсутствует, JVM автоматически добавит его.
Автоматически сгенерированное число зависит от компилятора. Это означает, что это может привести к маловероятному InvalidClassException .
Давайте проверим поведение, которое мы реализовали выше:
@Test public void whenSerializing_thenUseExternalizable() throws IOException, ClassNotFoundException { Country c = new Country(); c.setCode(374); c.setName("Armenia"); FileOutputStream fileOutputStream = new FileOutputStream(OUTPUT_FILE); ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); c.writeExternal(objectOutputStream); objectOutputStream.flush(); objectOutputStream.close(); fileOutputStream.close(); FileInputStream fileInputStream = new FileInputStream(OUTPUT_FILE); ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); Country c2 = new Country(); c2.readExternal(objectInputStream); objectInputStream.close(); fileInputStream.close(); assertTrue(c2.getCode() == c.getCode()); assertTrue(c2.getName().equals(c.getName())); }
В этом примере мы сначала создаем объект Country и записываем его в файл. Затем мы десериализуем объект из файла и проверяем правильность значений.
Вывод печатного c2 объекта:
Country{name='Armenia', code=374}
Это показывает, что мы успешно десериализовали объект.
2.2. Наследование
Когда класс наследуется от интерфейса Serializable , JVM автоматически собирает все поля из подклассов и делает их сериализуемыми.
Имейте в виду, что мы можем применить это и к Externalizable . Нам просто нужно реализовать методы чтения/записи для каждого подкласса иерархии наследования.
Давайте посмотрим на класс Region ниже, который расширяет наш класс Country из предыдущего раздела:
public class Region extends Country implements Externalizable { private static final long serialVersionUID = 1L; private String climate; private Double population; // getters, setters @Override public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal(out); out.writeUTF(climate); } @Override public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal(in); this.climate = in.readUTF(); } }
Здесь мы добавили два дополнительных свойства и сериализовали первое.
Обратите внимание, что мы также вызвали super.writeExternal(out), super.readExternal(in) внутри методов сериализатора для сохранения/восстановления полей родительского класса, а также .
Давайте запустим модульный тест со следующими данными:
Region r = new Region(); r.setCode(374); r.setName("Armenia"); r.setClimate("Mediterranean"); r.setPopulation(120.000);
Вот десериализованный объект:
Region{ country='Country{ name='Armenia', code=374}' climate='Mediterranean', population=null }
Обратите внимание, что поскольку мы не сериализовали поле population в классе Region , значение этого свойства равно null.
3. Externalizable vs Serializable
Давайте рассмотрим ключевые различия между этими двумя интерфейсами:
- Ответственность за сериализацию
Ключевое различие здесь заключается в том, как мы обрабатываем процесс сериализации. Когда класс реализует интерфейс java.io.Serializable , JVM берет на себя полную ответственность за сериализацию экземпляра класса. В случае Externalizable, именно программист должен позаботиться обо всей сериализации, а также о процессе десериализации.
- Пример использования
Если нам нужно сериализовать весь объект, то лучше всего подойдет интерфейс Serializable . С другой стороны, для пользовательской сериализации мы можем управлять процессом с помощью Externalizable .
- Представление
Интерфейс java.io.Serializable использует отражение и метаданные, что приводит к относительно низкой производительности. Для сравнения, интерфейс Externalizable дает вам полный контроль над процессом сериализации.
- Порядок чтения
При использовании Externalizable обязательно считывать все состояния полей в том же порядке , в каком они были записаны. В противном случае мы получим исключение.
Например, если мы изменим порядок чтения свойств code и name в классе Country , будет вызвано исключение java.io.EOFException .
Между тем, интерфейс Serializable не имеет такого требования.
- Пользовательская сериализация
Мы можем добиться пользовательской сериализации с помощью интерфейса Serializable , пометив поле ключевым словом transient . JVM не будет сериализовывать конкретное поле, но добавит его в файловое хранилище со значением по умолчанию . Вот почему рекомендуется использовать Externalizable в случае пользовательской сериализации.
4. Заключение
В этом кратком руководстве по интерфейсу Externalizable мы обсудили ключевые особенности, преимущества и продемонстрировали примеры простого использования. Мы также провели сравнение с интерфейсом Serializable .
Как обычно, полный исходный код учебника доступен на GitHub .