Serializable
Kinaya 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 Serializable
mayroon itong isang bilang ng mga disadvantages. Ilista natin ang ilan sa mga ito:
-
Pagganap. Ang interface ay may
Serializable
maraming mga pakinabang, ngunit ang mataas na pagganap ay malinaw na hindi isa sa kanila.
Una , ang panloob na mekanismo Serializable
ay 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 Serializable
ay 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.
-
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
transient
upang ibukod ang ilang data, at iyon lang. Parang "toolkit" :/ -
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
Serializable
hindi nito binibigyan ang pagkakataong ito.
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, Externalizable
dapat 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 out
at bilang mga parameter ObjectInput
na 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 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? Serializable
Nagtagumpay kami nang wala ito... :/ Narito tayo sa isa pang mahalagang nuance . Ang pagkakaiba sa pagitan ng Serializable
at Externalizable
namamalagi 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, Serializable
ang 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 Externalizable
mekanismo ng deserialization ay magkakaiba. Sa simula, ang default na constructor ay tinatawag. At pagkatapos lamang UserInfo
ay 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 Externalizable
ay dapat na may default na tagabuo . Idagdag natin ito sa ating klase UserInfo
at 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 Serializable
o Externalizable
), bigyang pansin ang mga variable static
. Kapag ginamit, Serializable
ang mga patlang na ito ay hindi naka-serialize sa lahat (at, nang naaayon, ang kanilang halaga ay hindi nagbabago, dahil static
ang mga patlang ay kabilang sa klase, hindi ang bagay). Ngunit kapag ginagamit ito, Externalizable
ikaw 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, Serializable
ang mga ito ay serialized at deserialized gaya ng dati, ngunit kapag ginamit, imposibleng Externalizable
deserialize final
ang isang variable ! Ang dahilan ay simple: ang lahat final
ng 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 final
ng -fields, gumamit ng karaniwang serialization sa pamamagitan ng Serializable
. Pangatlo , kapag gumagamit ng inheritance, ang lahat ng inheriting classes na nagmula sa ilang Externalizable
klase ay dapat ding may mga default na constructor. Narito ang ilang link sa magagandang artikulo tungkol sa mga mekanismo ng serialization:
See you! :)
GO TO FULL VERSION