JavaRush /Java Blog /Random-TL /Externalizable Interface sa Java

Externalizable Interface sa Java

Nai-publish sa grupo
Kamusta! Ngayon ay ipagpapatuloy namin ang aming panimula sa pagse-serialize at pag-deserialize ng mga bagay sa Java. Sa huling lecture, ipinakilala sa amin ang Serializable marker interface , tumingin sa mga halimbawa ng paggamit nito, at natutunan din kung paano kontrolin ang proseso ng serialization gamit ang transient keyword . Well, "pamahalaan ang proseso," siyempre, ay isang malakas na salita. Mayroon kaming isang keyword, isang bersyon ng ID, at iyon talaga. Ang natitirang bahagi ng proseso ay "naka-hardwired" sa loob ng Java, at walang access dito. Mula sa isang punto ng kaginhawaan, ito ay, siyempre, mabuti. Ngunit ang isang programmer sa kanyang trabaho ay dapat tumutok hindi lamang sa kanyang sariling kaginhawaan, tama ba? :) Mayroong iba pang mga kadahilanan upang isaalang-alang. Samakatuwid, ang Serializable ay hindi lamang ang tool para sa serialization-deserialization sa Java. Ngayon ay makikilala natin ang Externalizable interface . Ngunit bago pa man tayo magpatuloy sa pag-aaral nito, maaaring mayroon kang isang makatwirang tanong: bakit kailangan natin ng isa pang tool? SerializableKinaya ko ang aking trabaho, at ang awtomatikong pagpapatupad ng buong proseso ay hindi maaaring hindi magalak. Ang mga halimbawa na aming tiningnan ay hindi rin kumplikado. Kaya ano ang deal? Bakit isa pang interface para sa mahalagang parehong gawain? Ang katotohanan ay Serializablemayroon itong isang bilang ng mga disadvantages. Ilista natin ang ilan sa mga ito:
  1. Pagganap. Ang interface ay may Serializablemaraming mga pakinabang, ngunit ang mataas na pagganap ay malinaw na hindi isa sa kanila.

Ipinapakilala ang Externalizable Interface - 2

Una , ang panloob na mekanismo Serializableay bumubuo ng isang malaking halaga ng impormasyon ng serbisyo at iba't ibang uri ng pansamantalang data sa panahon ng operasyon.
Pangalawa (hindi mo na kailangang pumunta dito ngayon at magbasa sa iyong paglilibang kung interesado ka), ang gawain Serializableay batay sa paggamit ng Reflection API. Ang kagamitang ito ay nagpapahintulot sa iyo na gawin ang mga bagay na tila imposible sa Java: halimbawa, baguhin ang mga halaga ng mga pribadong field. Ang JavaRush ay may mahusay na artikulo tungkol sa Reflection API , maaari mong basahin ang tungkol dito.

  1. Kakayahang umangkop. Hindi namin kinokontrol ang proseso ng serialization-deserialization kapag ginagamit ang Serializable.

    Sa isang banda, ito ay napaka-maginhawa, dahil kung wala tayong pakialam sa pagganap, ang kakayahang hindi magsulat ng code ay tila maginhawa. Ngunit paano kung kailangan talaga naming magdagdag ng ilan sa aming sariling mga tampok (isang halimbawa ng isa sa mga ito ay nasa ibaba) sa serialization logic?

    Sa esensya, ang kailangan lang nating kontrolin ang proseso ay isang keyword transientupang ibukod ang ilang data, at iyon lang. Parang "toolkit" :/

  2. Kaligtasan. Ang puntong ito ay bahagyang sumusunod mula sa nauna.

    Hindi pa namin masyadong naiisip ang tungkol dito, ngunit paano kung ang ilang impormasyon sa iyong klase ay hindi inilaan para sa "mga tainga ng ibang tao" (mas tiyak, mga mata)? Ang isang simpleng halimbawa ay isang password o iba pang personal na data ng user, na sa modernong mundo ay kinokontrol ng isang grupo ng mga batas.

    Gamit ang Serializable, wala talaga kaming magagawa tungkol dito. I-serialize namin ang lahat ng bagay.

    Ngunit, sa mabuting paraan, dapat nating i-encrypt ang ganitong uri ng data bago ito isulat sa isang file o ipadala ito sa network. Ngunit Serializablehindi nito binibigyan ang pagkakataong ito.

Ipinapakilala ang Externalizable Interface - 3Well, sa wakas ay tingnan natin kung ano ang magiging hitsura ng isang klase gamit ang 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;

   //...конструктор, геттеры, сеттеры, toString()...

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

   }

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

   }
}
Gaya ng nakikita mo, nakagawa kami ng mga makabuluhang pagbabago! Ang pangunahing isa ay halata: kapag nagpapatupad ng isang interface, Externalizabledapat mong ipatupad ang dalawang mandatoryong pamamaraan - writeExternal()at readExternal(). Gaya ng sinabi namin kanina, lahat ng responsibilidad para sa serialization at deserialization ay nasa programmer. Gayunpaman, maaari mo na ngayong lutasin ang problema ng kawalan ng kontrol sa prosesong ito! Ang buong proseso ay direktang na-program mo, na, siyempre, ay lumilikha ng isang mas nababaluktot na mekanismo. Bilang karagdagan, ang problema sa seguridad ay nalutas din. Gaya ng nakikita mo, mayroon kaming field sa aming klase: personal na data na hindi maiimbak nang hindi naka-encrypt. Ngayon ay madali na tayong makakasulat ng code na nakakatugon sa hadlang na ito. Halimbawa, magdagdag ng dalawang simpleng pribadong pamamaraan sa aming klase para sa pag-encrypt at pag-decrypt ng lihim na data. Isusulat namin ang mga ito sa isang file at babasahin ang mga ito mula sa file sa naka-encrypt na form. At kami ay magsusulat at magbabasa ng natitirang data bilang ay :) Bilang isang resulta, ang aming klase ay magiging ganito ang hitsura:
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;
   }
}
Nagpatupad kami ng dalawang pamamaraan na gumagamit ng pareho ObjectOutput outat bilang mga parameter ObjectInputna nakatagpo na namin sa lecture tungkol sa Serializable. Sa tamang oras, ine-encrypt o i-decrypt namin ang kinakailangang data, at sa form na ito ginagamit namin ito para i-serialize ang aming object. Tingnan natin kung paano ito magiging hitsura sa pagsasanay:
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("Ivan", "Ivanov", "Ivan Ivanov's passport data");

       objectOutputStream.writeObject(userInfo);

       objectOutputStream.close();

   }
}
Sa encryptString()at mga pamamaraan decryptString(), partikular kaming nagdagdag ng output sa console upang suriin kung anong anyo ang isusulat at babasahin ng lihim na data. Ang code sa itaas ay naglalabas ng sumusunod na linya sa console: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Nagtagumpay ang pag-encrypt! Ang buong nilalaman ng file ay ganito ang hitsura: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Ngayon subukan nating gamitin ang deserialization logic na isinulat namin.
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();

   }
}
Buweno, mukhang walang anumang kumplikado dito, dapat itong gumana! Patakbuhin natin... Exception sa thread na "main" java.io.InvalidClassException: UserInfo; walang wastong konstruktor Ipinapakilala ang Externalizable Interface - 4 Oops :( Ito ay naging hindi gaanong simple! Ang mekanismo ng deserialization ay naghagis ng isang pagbubukod at hinihiling sa amin na lumikha ng isang default na konstruktor. Nagtataka ako kung bakit? SerializableNagtagumpay kami nang wala ito... :/ Narito tayo sa isa pang mahalagang nuance . Ang pagkakaiba sa pagitan ng Serializableat Externalizablenamamalagi hindi lamang sa "pinalawak" na pag-access para sa programmer at ang kakayahang mas flexible na pamahalaan ang proseso, kundi pati na rin sa proseso mismo. Una sa lahat, sa mekanismo ng deserialization ... Kapag ginamit, Serializableang memorya ay simple inilalaan para sa isang bagay, pagkatapos kung saan ang mga halaga ay binabasa mula sa stream, na pumupuno sa lahat ng mga patlang nito . Kung gagamitin natin Serializable, ang object constructor ay hindi tinatawag! Lahat ng trabaho ay ginagawa sa pamamagitan ng pagmuni-muni (Reflection API, na sa madaling sabi namin sa huling lecture). Sa kaso ng , ang Externalizablemekanismo ng deserialization ay magkakaiba. Sa simula, ang default na constructor ay tinatawag. At pagkatapos lamang UserInfoay tinatawag na sa nilikha object method readExternal(), na responsable para sa pagpuno sa mga field ng object. Iyon ay bakit ang anumang klase na nagpapatupad ng interface Externalizableay dapat na may default na tagabuo . Idagdag natin ito sa ating klase UserInfoat muling patakbuhin ang 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();
   }
}
Output ng console: Data ng pasaporte ni Ivan Ivanov UserInfo{firstName='Ivan', lastName='Ivanov', superSecretInformation='Data ng pasaporte ni Ivan Ivanov'} Isang ganap na kakaibang usapin! Una, ang na-decrypt na string na may lihim na data ay na-output sa console, at pagkatapos ay na-recover ang aming object mula sa file sa string na format! Ito ay kung paano namin matagumpay na nalutas ang lahat ng mga problema :) Ang paksa ng serialization at deserialization ay tila simple, ngunit tulad ng nakikita mo, ang aming mga lektura ay naging mahaba. At hindi lang iyon! Mayroong maraming higit pang mga subtleties kapag ginagamit ang bawat isa sa mga interface na ito, ngunit upang ngayon ang iyong utak ay hindi sumabog mula sa dami ng bagong impormasyon, maikli kong ililista ang ilang mas mahahalagang punto at magbigay ng mga link sa karagdagang pagbabasa. Kaya ano pa ang kailangan mong malaman? Una , kapag nagse-serialize (hindi mahalaga kung gagamit ka Serializableo Externalizable), bigyang pansin ang mga variable static. Kapag ginamit, Serializableang mga patlang na ito ay hindi naka-serialize sa lahat (at, nang naaayon, ang kanilang halaga ay hindi nagbabago, dahil staticang mga patlang ay kabilang sa klase, hindi ang bagay). Ngunit kapag ginagamit ito, Externalizableikaw mismo ang kumokontrol sa proseso, kaya sa teknikal na paraan, magagawa ito. Ngunit hindi ito inirerekomenda, dahil ito ay puno ng banayad na mga pagkakamali. Pangalawa , dapat ding bigyan ng pansin ang mga variable na may modifier final. Kapag ginamit, Serializableang mga ito ay serialized at deserialized gaya ng dati, ngunit kapag ginamit, imposibleng Externalizabledeserialize finalang isang variable ! Ang dahilan ay simple: ang lahat finalng mga patlang ay sinisimulan kapag ang default na tagabuo ay tinawag, at pagkatapos nito ay hindi na mababago ang kanilang halaga. Samakatuwid, upang i-serialize ang mga bagay na naglalaman finalng -fields, gumamit ng karaniwang serialization sa pamamagitan ng Serializable. Pangatlo , kapag gumagamit ng inheritance, ang lahat ng inheriting classes na nagmula sa ilang Externalizableklase ay dapat ding may mga default na constructor. Narito ang ilang link sa magagandang artikulo tungkol sa mga mekanismo ng serialization: See you! :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION