Serializable
ginawa ang trabaho nito, at ano ang hindi dapat mahalin tungkol sa awtomatikong pagpapatupad ng buong proseso? At ang mga halimbawa na aming tiningnan ay hindi rin kumplikado. So anong problema? Bakit kailangan natin ng isa pang interface para sa parehong mga gawain? Ang katotohanan ay Serializable
mayroon itong ilang mga pagkukulang. Inilista namin ang ilan sa kanila:
-
Pagganap. Ang
Serializable
interface ay may maraming mga pakinabang, ngunit ang mataas na pagganap ay malinaw na hindi isa sa kanila.Una,
Serializable
ang panloob na pagpapatupad ay bumubuo ng isang malaking halaga ng impormasyon ng serbisyo at lahat ng uri ng pansamantalang data.Pangalawa,
Serializable
umaasa sa Reflection API (hindi mo na kailangang sumisid ng malalim sa ngayon; maaari kang magbasa nang higit pa sa iyong paglilibang, kung interesado ka). Hinahayaan ka ng bagay na ito na gawin ang mga tila imposibleng bagay sa Java: halimbawa, baguhin ang mga halaga ng mga pribadong field. Ang CodeGym 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 ginamit namin ang
Serializable
interface.Sa isang banda, ito ay napaka-maginhawa, dahil kung hindi tayo partikular na nag-aalala tungkol sa pagganap, kung gayon mukhang maganda na hindi kailangang magsulat ng code. Ngunit paano kung kailangan talaga naming magdagdag ng ilan sa aming sariling mga tampok (magbibigay kami ng isang halimbawa sa ibaba) sa serialization logic?
Karaniwan, ang kailangan lang nating kontrolin ang proseso ay ang
transient
keyword upang ibukod ang ilang data. Ayan yun. Iyan ang aming buong toolbox :/ -
Seguridad. Ang item na ito ay nakukuha sa bahagi mula sa nakaraang item.
Wala pa kaming gaanong oras sa pag-iisip tungkol dito, ngunit paano kung ang ilang impormasyon sa iyong klase ay hindi para sa mga mata at tainga ng iba? Ang isang simpleng halimbawa ay isang password o iba pang personal na data ng user, na sa mundo ngayon ay pinamamahalaan ng isang grupo ng mga batas.
Kung gagamitin natin
Serializable
, wala talaga tayong magagawa tungkol dito. I-serialize namin ang lahat nang ganito.Ngunit kung gagawin natin ito sa tamang paraan, dapat nating i-encrypt ang ganitong uri ng data bago ito isulat sa isang file o ipadala ito sa isang network. Ngunit
Serializable
hindi ito ginagawang posible.

Externalizable
interface.
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 {
}
}
Tulad ng nakikita mo, mayroon kaming mga makabuluhang pagbabago! Ang pangunahing isa ay halata: kapag nagpapatupad ng Externalizable
interface, dapat mong ipatupad ang dalawang kinakailangang pamamaraan: writeExternal()
atreadExternal()
. Gaya ng sinabi namin kanina, ang responsibilidad para sa serialization at deserialization ay nasa programmer. Ngunit ngayon maaari mong malutas ang problema ng walang kontrol sa proseso! Ang buong proseso ay direktang na-program mo. Naturally, pinapayagan nito ang isang mas nababaluktot na mekanismo. Bukod pa rito, nalutas ang problema sa seguridad. Gaya ng nakikita mo, ang aming klase ay may personal na field ng data na hindi maiimbak nang hindi naka-encrypt. Ngayon ay madali na tayong makakasulat ng code na nakakatugon sa hadlang na ito. Halimbawa, maaari kaming magdagdag sa aming klase ng dalawang simpleng pribadong pamamaraan upang i-encrypt at i-decrypt ang sensitibong data. Isusulat namin ang data sa file at babasahin ito mula sa file sa naka-encrypt na form. Ang natitirang data ay isusulat at babasahin kung ano ito :) Bilang resulta, ganito ang hitsura ng aming klase:
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;
}
}
Ipinatupad namin ang dalawang pamamaraan na gumagamit ng pareho ObjectOutput
at ObjectInput
mga parameter na nakilala na namin sa aralin tungkol sa Serializable
. Sa tamang sandali, ine-encrypt o i-decrypt namin ang kinakailangang data, at ginagamit namin ang naka-encrypt na data para i-serialize ang aming object. Tingnan natin kung ano ang hitsura nito 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("Paul", "Piper", "Paul Piper's passport data");
objectOutputStream.writeObject(userInfo);
objectOutputStream.close();
}
}
Sa encryptString()
at decryptString()
mga pamamaraan, partikular kaming nagdagdag ng console output upang i-verify ang form kung saan isusulat at babasahin ang lihim na data. Ang code sa itaas ay nagpakita ng sumusunod na linya: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Nagtagumpay ang pag-encrypt! Ang buong nilalaman ng file ay ganito ang hitsura: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx Ngayon ay subukan nating gamitin ang aming deserialization logic.
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();
}
}
Well, mukhang walang kumplikado dito. Dapat itong gumana! Pinapatakbo namin ito at nakakuha ng... Exception sa thread na "main" java.io.InvalidClassException: UserInfo; walang wastong constructor 
Serializable
nalampasan namin nang walang isa... :/ Dito ay nakatagpo kami ng isa pang mahalagang nuance. Ang ang pagkakaiba sa pagitan ng Serializable
at Externalizable
namamalagi hindi lamang sa 'pinalawak' na pag-access ng programmer at ang kakayahang mas madaling makontrol ang proseso, kundi pati na rin sa proseso mismo. Higit sa lahat, sa mekanismo ng deserialization . Kapag gumagamitSerializable
, ang memorya ay inilalaan lamang para sa object, at pagkatapos ay binabasa ang mga value mula sa stream at ginagamit upang itakda ang mga field ng object. Kung gagamitin natin ang Serializable
, hindi tinatawag ang constructor ng object! Ang lahat ng gawain ay nangyayari sa pamamagitan ng pagmuni-muni (ang Reflection API, na maikling binanggit namin sa huling aralin). Sa Externalizable
, iba ang mekanismo ng deserialization. Ang default na tagabuo ay unang tinawag. Pagkatapos lamang nito ay tinawag ang pamamaraan UserInfo
ng nilikha na bagay readExternal()
. Ito ay responsable para sa pagtatakda ng mga patlang ng bagay. Iyon ang dahilan kung bakit ang anumang klase na nagpapatupad ng Externalizable
interface ay dapat na may default na tagabuo . Magdagdag tayo ng isa sa aming UserInfo
klase 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 Paul Piper UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'data ng pasaporte ni Paul Piper' } Ngayon ay isang bagay na ganap na naiiba! Una, ang na-decrypt na string na may lihim na impormasyon ay ipinakita sa console. Pagkatapos ang bagay na nakuha namin mula sa file ay ipinakita bilang isang string! Kaya't matagumpay naming nalutas ang lahat ng mga problema :) Ang paksa ng serialization at deserialization ay tila simple, ngunit, tulad ng nakikita mo, ang mga aralin ay mahaba. At marami pa tayong hindi natalakay! Mayroon pa ring maraming mga subtleties na kasangkot kapag ginagamit ang bawat isa sa mga interface na ito. Ngunit upang maiwasang sumabog ang iyong utak mula sa labis na bagong impormasyon, maikli kong ilista ang ilang mas mahahalagang punto at bibigyan ka ng mga link sa karagdagang pagbabasa. Kaya, ano pa ang kailangan mong malaman? Una , sa panahon ng serialization (hindi alintana kung gumagamit ka man Serializable
o Externalizable
), bigyang pansin ang static
mga variable. Kapag ginamit mo ang Serializable
, ang mga patlang na ito ay hindi naka-serialize sa lahat (at, nang naaayon, ang kanilang mga halaga ay hindi nagbabago, dahil ang static
mga patlang ay kabilang sa klase, hindi ang bagay). Pero kapag ginamit moExternalizable
, ikaw mismo ang kumokontrol sa proseso, kaya sa teknikal na paraan maaari mong i-serialize ang mga ito. Ngunit, hindi namin ito inirerekomenda, dahil ang paggawa ay malamang na lumikha ng maraming banayad na mga bug. Pangalawa , dapat mo ring bigyang pansin ang mga variable na may final
modifier. Kapag ginamit mo ang Serializable
, ang mga ito ay serialized at deserialized gaya ng dati, ngunit kapag ginamit mo ang Externalizable
, imposibleng deserialize ang isang final
variable ! Ang dahilan ay simple: ang lahat final
ng mga patlang ay sinisimulan kapag ang default na tagabuo ay tinawag - pagkatapos nito, ang kanilang halaga ay hindi na mababago. Samakatuwid, upang i-serialize ang mga bagay na may final
mga field, gamitin ang karaniwang serialization na ibinigay ng Serializable
. Pangatlo , kapag gumamit ka ng inheritance, lahat ng descendant classes na nagmamana ng ilanExternalizable
ang klase ay dapat ding may mga default na konstruktor. Narito ang link sa magandang artikulo tungkol sa mga mekanismo ng serialization:
Hanggang sa muli! :)
GO TO FULL VERSION