Пример Java Externalizable – Более эффективная сериализация

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

Чтобы решить эту проблему, мы можем написать пользовательскую логику сериализации и десериализации, реализовав интерфейс Externalizable и переопределив его методы writeExternal() и readExternal(). Реализуя эти методы, мы сообщаем JVM, как кодировать/декодировать объект.

public interface Externalizable {void readExternal(ObjectInput in)void writeExternal(ObjectOutput out)}

1. Использование внешнего интерфейса

В качестве примера мы создали класс UserSettings, который будем сериализовать и десериализовать с помощью методов writeExternal() и readExternal().

В следующем классе мы не сериализуем и не десериализуем поле 'doNotStoreMe'. Также рассмотрите возможность добавления поля serialVersionUID, чтобы избежать InvalidClassException, когда мы вносим несовместимые изменения в структуру класса.

public class UserSettings implements Externalizable {private static final long serialVersionUID = 1L;public UserSettings(){}private String doNotStoreMe;private Integer fieldOne;private String fieldTwo;private boolean fieldThree;public String getDoNotStoreMe() {return doNotStoreMe;}public void setDoNotStoreMe(String doNotStoreMe) {this.doNotStoreMe = doNotStoreMe;}public Integer getFieldOne() {return fieldOne;}public void setFieldOne(Integer fieldOne) {this.fieldOne = fieldOne;}public String getFieldTwo() {return fieldTwo;}public void setFieldTwo(String fieldTwo) {this.fieldTwo = fieldTwo;}public boolean isFieldThree() {return fieldThree;}public void setFieldThree(boolean fieldThree) {this.fieldThree = fieldThree;}public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {fieldOne = in.readInt();fieldTwo = in.readUTF();fieldThree = in.readBoolean();}public void writeExternal(ObjectOutput out) throws IOException {out.writeInt(fieldOne);out.writeUTF(fieldTwo);out.writeBoolean(fieldThree);}@Overridepublic String toString() {return "UserSettings [doNotStoreMe=" + doNotStoreMe + ", fieldOne="+ fieldOne + ", fieldTwo=" + fieldTwo + ", fieldThree="+ fieldThree + "]";}}

1.1. Пример Externalizable.writeExternal()

Метод writeExternal() обеспечивает логику для сериализации, т.е. записи полей класса в байты. Мы можем хранить только те поля, которые хотим вернуть после чтения сериализованного объекта. Игнорируйте остальные поля.

public void writeExternal(ObjectOutput out) throws IOException {//We are not storing the field 'doNotStoreMe'out.writeInt(fieldOne);out.writeUTF(fieldTwo);out.writeBoolean(fieldThree);}

1.2. Пример Externalizable.readExternal()

Единственное, что нам нужно помнить, это то, что метод readExternal() должен десериализовать значения в той же последовательности и с теми же типами, которые были записаны методом writeExternal().

public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {fieldOne = in.readInt();fieldTwo = in.readUTF();fieldThree = in.readBoolean();}

2. Полный пример

Теперь давайте напишем код для сериализации и обратного считывания байтов, чтобы проверить, соблюдает ли JVM контракт.

public class ExternalizableExample {public static void main(String[] args) {UserSettings settings = new UserSettings();settings.setDoNotStoreMe("Sensitive info");settings.setFieldOne(10000);settings.setFieldTwo("HowToDoInJava.com");settings.setFieldThree(false);//BeforeSystem.out.println(settings);//AfterstoreUserSettings(settings);UserSettings loadedSettings = loadSettings();System.out.println(loadedSettings);}private static UserSettings loadSettings() {try {FileInputStream fis = new FileInputStream("object.ser");ObjectInputStream ois = new ObjectInputStream(fis);UserSettings settings = (UserSettings) ois.readObject();ois.close();return settings;} catch(IOException e) {e.printStackTrace();} catch(ClassNotFoundException e) {e.printStackTrace();}return null;}private static void storeUserSettings(UserSettings settings){try {FileOutputStream fos = new FileOutputStream("object.ser");ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(settings);oos.close();} catch(IOException e) {e.printStackTrace();}}}

Вывод программы:

UserSettings [doNotStoreMe=Sensitive info, fieldOne=10000, fieldTwo=HowToDoInJava.com, fieldThree=false]UserSettings [doNotStoreMe=null, fieldOne=10000, fieldTwo=HowToDoInJava.com, fieldThree=false]

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

3. Разница между Экстернализируемым и Сериализуемым

В следующих таблицах обобщены основные различия между интерфейсами Serializable и Externalizable:

Особенность Сериализуемый Экстернализируемый
(Де)сериализация выполнена? JVM несет полную ответственность за выполнение сериализации и десериализации. Программист должен написать пользовательскую логику для сериализации и десериализации.
Когда использовать? Необходимо сериализовать весь объект. Для частичной сериализации.
Производительность Относительно медленно из-за отражения во время выполнения. Быстро за счет полного контроля над процессом.
Порядок чтения/записи Заказ не требуется. Читать следует в том же порядке, в котором были написаны поля.
Прокрутить вверх