CodeGym/Java блог/Случаен/Externalizable интерфейс в Java
John Squirrels
Ниво
San Francisco

Externalizable интерфейс в Java

Публикувано в групата
здрасти Днес ще продължим да се запознаваме със сериализацията и десериализацията на Java обекти. В последния урок се запознахме с интерфейса на маркера Serializable , прегледахме примери за използването му и също така научихме How можете да използвате ключовата дума transient , за да контролирате процеса на сериализация. Е, твърдението, че ние „контролираме процеса“, може да е преувеличено. Имаме една ключова дума, един идентификатор на versionта и това е всичко. Останалата част от процеса е скрита в Java и нямаме достъп до нея. Разбира се, от гледна точка на удобството това е добре. Но програмистът не трябва да се ръководи само от собствения си комфорт, нали? :) Има и други фактори, които трябва да имате предвид. Ето защо Serializableне е единственият механизъм за сериализация-десериализация в Java. Днес ще се запознаем с интерфейса Externalizable . Но преди да започнем да го изучаваме, може би имате разумен въпрос: защо се нуждаем от друг механизъм? Serializableсвърши работата си и Howво не харесваме в автоматичното изпълнение на целия процес? И примерите, които разгледахме, също бяха несложни. Та Howъв е проблема? Защо се нуждаем от друг интерфейс за по същество същите задачи? Факт е, че Serializableима няколко недостатъка. Ние изброяваме някои от тях:
  1. Производителност. Интерфейсът Serializableима много предимства, но високата производителност очевидно не е едно от тях.

    Представяне на външния интерфейс - 2

    Първо, Serializable вътрешното внедряване на генерира голямо количество служебна информация и всяHowви временни данни.

    Второ, Serializable разчита на Reflection API (не е нужно да се гмуркате дълбоко в това точно сега; можете да прочетете повече в свободното си време, ако се интересувате). Това нещо ви позволява да правите привидно невъзможните неща в Java: например да променяте стойностите на частните полета. CodeGym има отлична статия за Reflection API . Можете да прочетете за това там.

  2. Гъвкавост. Ние не контролираме процеса на сериализация-десериализация, когато използваме интерфейса Serializable.

    От една страна, това е много удобно, защото ако не сме особено загрижени за производителността, тогава изглежда хубаво да не се налага да пишем code. Но Howво ще стане, ако наистина трябва да добавим някои от нашите собствени функции (ще предоставим пример по-долу) към логиката на сериализация?

    По принцип всичко, което трябва да контролираме процеса, е transientключовата дума за изключване на някои данни. Това е. Това е цялата ни кутия с инструменти :/

  3. Сигурност. Този елемент произлиза отчасти от предишния елемент.

    Не сме отделяли много време да мислим за това преди, но Howво ще стане, ако част от информацията във вашия клас не е предназначена за любопитни очи и уши на другите? Прост пример е парола or други лични потребителски данни, които в днешния свят се управляват от куп закони.

    Ако използваме Serializable, всъщност не можем да направим нищо по въпроса. Ние сериализираме всичко, Howто е.

    Но ако го направим по правилния начин, трябва да криптираме този вид данни, преди да ги запишем във файл or да ги изпратим по мрежа. Но Serializableне прави това възможно.

Представяне на външния интерфейс - 3Е, нека най-накрая да видим How ще изглежда класът, ако използваме интерфейса Externalizable.
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;

public class UserInfo implements Externalizable {

   private String firstName;
   private String lastName;
   private String superSecretInformation;

private static final long SERIAL_VERSION_UID = 1L;

   // ...constructor, getters, setters, toString()...

   @Override
   public void writeExternal(ObjectOutput out) throws IOException {

   }

   @Override
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

   }
}
Както можете да видите, имаме значителни промени! Основният е очевиден: когато внедрявате интерфейса Externalizable, трябва да приложите два задължителни метода: writeExternal()иreadExternal(). Както казахме по-рано, отговорността за сериализацията и десериализацията ще бъде на програмиста. Но сега можете да разрешите проблема с липсата на контрол върху процеса! Целият процес се програмира директно от вас. Естествено, това позволява много по-гъвкав механизъм. Освен това проблемът със сигурността е решен. Както можете да видите, нашият клас има поле за лични данни, което не може да се съхранява некриптирано. Сега можем лесно да напишем code, който удовлетворява това ограничение. Например, можем да добавим към нашия клас два прости частни метода за криптиране и декриптиране на чувствителни данни. Ще запишем данните във file и ще ги прочетем от file в криптирана форма. Останалите данни ще бъдат записани и прочетени така, Howто са :) В резултат нашият клас изглежда така:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Base64;

public class UserInfo implements Externalizable {

   private String firstName;
   private String lastName;
   private String superSecretInformation;

   private static final long serialVersionUID = 1L;

   public UserInfo() {
   }

   public UserInfo(String firstName, String lastName, String superSecretInformation) {
       this.firstName = firstName;
       this.lastName = lastName;
       this.superSecretInformation = superSecretInformation;
   }

   @Override
   public void writeExternal(ObjectOutput out) throws IOException {
       out.writeObject(this.getFirstName());
       out.writeObject(this.getLastName());
       out.writeObject(this.encryptString(this.getSuperSecretInformation()));
   }

   @Override
   public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
       firstName = (String) in.readObject();
       lastName = (String) in.readObject();
       superSecretInformation = this.decryptString((String) in.readObject());
   }

   private String encryptString(String data) {
       String encryptedData = Base64.getEncoder().encodeToString(data.getBytes());
       System.out.println(encryptedData);
       return encryptedData;
   }

   private String decryptString(String data) {
       String decrypted = new String(Base64.getDecoder().decode(data));
       System.out.println(decrypted);
       return decrypted;
   }

   public String getFirstName() {
       return firstName;
   }

   public String getLastName() {
       return lastName;
   }

   public String getSuperSecretInformation() {
       return superSecretInformation;
   }
}
Реализирахме два метода, които използват едни ObjectOutputи същи ObjectInputпараметри, които вече срещнахме в урока за Serializable. В точния момент ние криптираме or декриптираме необходимите данни и използваме криптираните данни, за да сериализираме нашия обект. Нека да видим How изглежда това на практика:
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);

       UserInfo userInfo = new UserInfo("Paul", "Piper", "Paul Piper's passport data");

       objectOutputStream.writeObject(userInfo);

       objectOutputStream.close();

   }
}
В методите encryptString()и decryptString()ние специално добавихме конзолен изход, за да проверим формата, в която секретните данни ще бъдат записани и прочетени. Кодът по-горе показва следния ред: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Шифроването е успешно! Пълното съдържание на file изглежда така: ¬n sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Сега нека опитаме да използваме нашата логика за десериализация.
public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);


       UserInfo userInfo = (UserInfo) objectInputStream.readObject();
       System.out.println(userInfo);

       objectInputStream.close();

   }
}
Е, тук нищо не изглежда сложно. Би трябвало да работи! Пускаме го и получаваме... Изключение в нишката "main" java.io.InvalidClassException: UserInfo; няма валиден конструктор Представяне на външния интерфейс - 4 Ами сега! :( Очевидно не е толкова лесно! Механизмът за десериализация хвърли изключение и изискваше да създадем конструктор по подразбиране. Чудя се защо. С Serializable, се разминахме без такъв... :/ Тук се натъкнахме на друг важен нюанс. разликата между Serializableи Externalizableе не само в "разширения" достъп на програмиста и възможността за по-гъвкав контрол на процеса, но и в самия процес. Преди всичко в механизма за десериализация . При използванеSerializable, паметта просто се разпределя за обекта и след това стойностите се четат от потока и се използват за задаване на полетата на обекта. Ако използваме Serializable, конструкторът на обекта не се извиква! Цялата работа се случва чрез отражение (API Reflection, което споменахме накратко в последния урок). При Externalizable, механизмът за десериализация е различен. Първо се извиква конструкторът по подразбиране. Едва след това се извиква методът на създадения UserInfoобект readExternal(). Той отговаря за настройката на полетата на обекта. Ето защо всеки клас, реализиращ Externalizableинтерфейса, трябва да има конструктор по подразбиране . Нека добавим един към нашия UserInfoклас и повторим codeа:
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException, ClassNotFoundException {

       FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);


       UserInfo userInfo = (UserInfo) objectInputStream.readObject();
       System.out.println(userInfo);

       objectInputStream.close();
   }
}
Изход от конзолата: паспортните данни на Пол Пайпър UserInfo \ firstName = 'Пол', фамorя = 'Пайпър', superSecretInformation = 'Паспортните данни на Пол Пайпър' } Това е нещо съвсем различно! Първо дешифрираният низ със секретна информация се показва на конзолата. Тогава обектът, който възстановихме от file, беше показан като низ! Така че успешно решихме всички проблеми :) Темата за сериализацията и десериализацията изглежда проста, но, Howто виждате, уроците бяха дълги. И има толкова много повече, които не сме покрor! Все още има много тънкости, свързани с използването на всеки от тези интерфейси. Но за да избегна експлозия на мозъка ви от прекомерна нова информация, ще изброя накратко още няколко важни точки и ще ви дам връзки към допълнителна литература. И така, Howво още трябва да знаете? Първо , по време на сериализация (независимо дали използвате Serializableor Externalizable), обърнете внимание на staticпроменливите. Когато използвате Serializable, тези полета изобщо не се сериализират (и съответно стойностите им не се променят, тъй като staticполетата принадлежат на класа, а не на обекта). Но когато използватеExternalizable, вие сами контролирате процеса, така че технически можете да ги сериализирате. Но ние не го препоръчваме, тъй като има вероятност да създадете много фини грешки. Второ , трябва да обърнете внимание и на променливите с finalмодификатора. Когато използвате Serializable, те се сериализират и десериализират Howто обикновено, но когато използвате Externalizable, е невъзможно да десериализирате finalпроменлива ! Причината е проста: всички finalполета се инициализират, когато се извика конструкторът по подразбиране - след това стойността им не може да бъде променена. Следователно, за да сериализирате обекти, които имат finalполета, използвайте стандартната сериализация, предоставена от Serializable. Трето , когато използвате наследяване, всички класове наследници, които наследяват някоиExternalizableкласът също трябва да има конструктори по подразбиране. Ето връзка към добра статия за механизмите за сериализация: До следващия път! :)
Коментари
  • Популярен
  • Нов
  • Стар
Трябва да сте влезли, за да оставите коментар
Тази страница все още няма коментари