Руководство по внешнему интерфейсу в Java

Руководство по интерфейсу Externalizable в Java

1. Вступление

В этом руководствеwe’ll have a quick look at java’s java.io.Externalizable interface. Основная цель этого интерфейса - облегчить пользовательскую сериализацию и десериализацию.

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

После этого мы собираемся обсудить основные отличия от интерфейсаjava.io.Serializable.

2. The Externalizable Интерфейс

Externalizable происходит от интерфейса маркераjava.io.Serializable. Any class that implements Externalizable interface should override the writeExternal(), readExternal() methods. Таким образом, мы можем изменить поведение сериализации 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 и реализует два упомянутых выше метода.

In the writeExternal() method, we’re adding the object’s properties to the ObjectOutput stream. Это стандартные методы, такие какwriteUTF() дляString иwriteInt() для значений int.

Затемfor deserializing the object, we’re reading from the ObjectInput stream использует методыreadUTF(), readInt() для чтения свойств в том же точном порядке, в котором они были записаны.

Рекомендуется добавлять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. We just need to implement the read/write methods for every sub-class of the inheritance hierarchy.

Давайте посмотрим на класс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();
    }
}

Здесь мы добавили два дополнительных свойства и сериализовали первое.

Обратите внимание, чтоwe also called super.writeExternal(out), super.readExternal(in) within serializer methods to save/restore the parent class fields as well.

Давайте запустим модульный тест со следующими данными:

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
}

Обратите внимание, чтоsince we didn’t serialize the population field in Region class, the value of that property is null.

3. Externalizable противSerializable

Давайте рассмотрим основные различия между двумя интерфейсами:

  • Ответственность за сериализацию

Ключевое отличие здесь заключается в том, как мы управляем процессом сериализации. Когда класс реализует интерфейсjava.io.Serializable, JVM берет на себя полную ответственность за сериализацию экземпляра класса. In case of Externalizable, it’s the programmer who should take care of the whole serialization and also deserialization process.

  • Случай использования

Если нам нужно сериализовать весь объект, интерфейсSerializable лучше подходит. С другой стороны,for custom serialization, we can control the process using Externalizable.

  • Спектакль

Интерфейсjava.io.Serializable использует отражение и метаданные, что приводит к относительно низкой производительности. Для сравнения:Externalizable interface gives you full control over the serialization process.

  • Порядок чтения

While using Externalizable, it’s mandatory to read all the field states in the exact order as they were written. В противном случае мы получим исключение.

Например, если мы изменим порядок чтения свойствcode иname в классеCountry, будет брошенjava.io.EOFException.

Между тем, для интерфейсаSerializable этого требования нет.

  • Пользовательская сериализация

Мы можем добиться настраиваемой сериализации с помощью интерфейсаSerializable, пометив поле ключевым словомtransient. The JVM won’t serialize the particular field but it’ll add up the field to file storage with the default value. Вот почему рекомендуется использоватьExternalizable в случае пользовательской сериализации.

4. Заключение

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

Как обычно, доступен полный исходный код учебникаover on GitHub.