Ръководство за интерфейс за екстернализация в Java

1. Въведение

В този урок ще разгледаме набързо интерфейса java.io.Externalizable на java . Основната цел на този интерфейс е да улесни персонализирана сериализация и десериализация.

Преди да продължим, не забравяйте да проверите статията за сериализацията в Java. Следващата глава е за това как да сериализирате Java обект с този интерфейс.

След това ще обсъдим ключовите разлики в сравнение с интерфейса java.io.Serializable .

2. Екстернализираният интерфейс

Externalizable се простира от java.io.Serializable маркер интерфейс. Всеки клас, който реализира 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(); } }

Ето, ние сме определили клас Държава , който реализира Externalizable интерфейс и изпълнява двата метода, посочени по-горе.

В метода writeExternal () добавяме свойствата на обекта към потока ObjectOutput . Това има стандартни методи като writeUTF () за String и writeInt () за стойностите int.

След това, за десериализиране на обекта, четем от потока ObjectInput , използвайки методите 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. Наследяване

Когато клас наследява от интерфейса за сериализиране , 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 }

Забележете, че тъй като не сме сериализирали полето за популация в клас Region , стойността на това свойство е нула.

3. Възможност за екстернализация срещу сериализация

Нека да разгледаме ключовите разлики между двата интерфейса:

  • Отговорност за сериализацията

Ключовата разлика тук е как се справяме с процеса на сериализация. Когато клас реализира интерфейса java.io.Serializable , JVM поема пълната отговорност за сериализиране на екземпляра на класа. В случай на Externalizable, програмистът трябва да се погрижи за целия процес на сериализация, а също и десериализация.

  • Случай за употреба

Ако трябва да сериализираме целия обект, интерфейсът за сериализиране е по-подходящ. От друга страна, за персонализирана сериализация, можем да контролираме процеса, използвайки Externalizable .

  • производителност

Интерфейсът java.io.Serializable използва отражение и метаданни, което причинява относително бавна производителност. За сравнение, интерфейсът Externalizable ви дава пълен контрол върху процеса на сериализация.

  • Ред за четене

Докато използвате Externalizable , е задължително да прочетете всички състояния на полетата в точния ред, както са били написани. В противен случай ще получим изключение.

Например, ако променим реда на четене на свойствата на кода и имената в класа Country , ще бъде хвърлен java.io.EOFException .

Междувременно сериализиращият интерфейс няма това изискване.

  • Персонализирана сериализация

Можем да постигнем персонализирана сериализация с интерфейса за сериализиране, като маркираме полето с преходна ключова дума. JVM няма да сериализира конкретното поле, но ще добави полето за съхранение на файлове със стойността по подразбиране . Ето защо е добра практика да се използва Externalizable в случай на персонализирана сериализация.

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

В това кратко ръководство на Externalizable интерфейса обсъдихме основните характеристики, предимства и показват примери на прости употреба. Направихме и сравнение с интерфейса Serializable .

Както обикновено, пълният изходен код на урока е достъпен в GitHub.