CodeGym /Java Blog /Random /Ano ang pagkakaiba sa pagitan ng serialization at deseria...
John Squirrels
Antas
San Francisco

Ano ang pagkakaiba sa pagitan ng serialization at deserialization sa Java?

Nai-publish sa grupo
Hi! Sa aralin ngayon, pinag-uusapan natin ang serialization at deserialization sa Java. Magsisimula tayo sa isang simpleng halimbawa. Sabihin nating nakagawa ka ng laro sa computer. Kung lumaki ka noong 90s at naaalala mo ang mga game console noong panahong iyon, malamang na alam mo na kulang sila sa isang bagay na pinababayaan natin ngayon — ang kakayahang mag-save at mag-load ng mga laro :) Kung hindi, isipin mo iyon! Ano ang pagkakaiba sa pagitan ng serialization at deserialization sa Java?  - 1 Natatakot ako na ang larong walang ganitong mga kakayahan ay mapapahamak ngayon! Ano ang ibig sabihin ng "mag-save" at "mag-load" ng laro? Well, naiintindihan namin ang karaniwang kahulugan: gusto naming ipagpatuloy ang laro mula sa lugar kung saan kami tumigil. Upang gawin ito, lumikha kami ng isang uri ng "check point", na pagkatapos ay gagamitin namin upang i-load ang laro. Ngunit ano ang ibig sabihin nito sa isang programmer sa halip na isang kaswal na gamer? Ang sagot ay simple: kami'. Sabihin nating naglalaro ka bilang Spain sa Strategium. Ang iyong laro ay may estado: kung sino ang nagmamay-ari kung aling mga teritoryo, sino ang may kung gaano karaming mga mapagkukunan, sino ang nasa isang alyansa kung kanino, sino ang nakikipagdigma kung kanino, at iba pa. Dapat nating i-save ang impormasyong ito, ang estado ng ating programa, upang maibalik ito sa hinaharap at maipagpatuloy ang laro. Para sa ito ay tiyak kung para saan ang serialization at deserealization . Ang serialization ay ang proseso ng pag-iimbak ng estado ng isang bagay sa isang pagkakasunud-sunod ng mga byte. Deserializationay ang proseso ng pagpapanumbalik ng isang bagay mula sa mga byte na ito. Anumang Java object ay maaaring ma-convert sa isang byte sequence. Bakit natin kakailanganin iyon? Sinabi namin nang higit sa isang beses na ang mga programa ay hindi umiiral sa kanilang sarili. Kadalasan, nakikipag-ugnayan sila sa iba pang mga programa, nagpapalitan ng data, atbp. At ang isang byte sequence ay isang maginhawa at mahusay na format. Halimbawa, maaari nating SavedGamegawing isang sequence ng mga byte ang ating object, ipadala ang mga byte na ito sa network patungo sa isa pang computer, at pagkatapos ay sa pangalawang computer, gawing Java object ang mga byte na ito! Mukhang mahirap, tama? At ang pagpapatupad ng prosesong ito ay tila isang sakit :/ Sa kabutihang palad, hindi ito ganoon! :) Sa Java, angSerializableang interface ay responsable para sa proseso ng serialization. Napakasimple ng interface na ito: hindi mo kailangang magpatupad ng isang paraan para magamit ito! Ganito kasimple ang hitsura ng aming klase sa pag-save ng laro:

import java.io.Serializable;
import java.util.Arrays;

public class SavedGame implements Serializable {

   private static final long serialVersionUID = 1L;

   private String[] territoriesInfo;
   private String[] resourcesInfo;
   private String[] diplomacyInfo;

   public SavedGame(String[] territoriesInfo, String[] resourcesInfo, String[] diplomacyInfo){
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public String[] getTerritoriesInfo() {
       return territoriesInfo;
   }

   public void setTerritoriesInfo(String[] territoriesInfo) {
       this.territoriesInfo = territoriesInfo;
   }

   public String[] getResourcesInfo() {
       return resourcesInfo;
   }

   public void setResourcesInfo(String[] resourcesInfo) {
       this.resourcesInfo = resourcesInfo;
   }

   public String[] getDiplomacyInfo() {
       return diplomacyInfo;
   }

   public void setDiplomacyInfo(String[] diplomacyInfo) {
       this.diplomacyInfo = diplomacyInfo;
   }

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoriesInfo=" + Arrays.toString(territoriesInfo) +
               ", resourcesInfo=" + Arrays.toString(resourcesInfo) +
               ", diplomacyInfo=" + Arrays.toString(diplomacyInfo) +
               '}';
   }
}
Ang tatlong array ay responsable para sa impormasyon tungkol sa mga teritoryo, mapagkukunan, at diplomasya. Ang Serializable interface ay nagsasabi sa Java virtual machine: " Lahat ay OK — kung kinakailangan, ang mga bagay ng klase na ito ay maaaring serialized ". Ang isang interface na walang isang interface ay mukhang kakaiba :/ Bakit ito kinakailangan? Ang sagot sa tanong na ito ay makikita sa itaas: nagsisilbi lamang itong magbigay ng kinakailangang impormasyon sa Java virtual machine. Sa isa sa aming mga nakaraang aralin, maikling binanggit namin ang mga marker interface . Ito ay mga espesyal na interface ng impormasyon na nagmamarka lamang sa aming mga klase ng karagdagang impormasyon na magiging kapaki-pakinabang sa Java machine sa hinaharap. Wala silang anumang mga pamamaraan na kailangan mong ipatupad.Serializableay isa sa mga interface na iyon. Isa pang mahalagang punto: Bakit kailangan natin ang private static final long serialVersionUIDvariable na tinukoy natin sa klase? Bakit kailangan ito? Naglalaman ang field na ito ng natatanging identifier para sa bersyon ng serialized na klase . Anumang klase na nagpapatupad ng Serializableinterface ay may versionidentifier. Kinakalkula ito batay sa mga nilalaman ng klase: ang mga patlang nito, ang pagkakasunud-sunod kung saan idineklara ang mga ito, mga pamamaraan, atbp. Kung babaguhin natin ang uri ng patlang at/o ang bilang ng mga patlang sa ating klase, agad na magbabago ang identifier ng bersyon . serialVersionUIDay nakasulat din kapag ang klase ay serialized. Kapag sinubukan naming i-deserialize, iyon ay, ibalik ang isang bagay mula sa isang hanay ng mga byte, ang nauugnay serialVersionUIDay inihambing sa halaga ngserialVersionUIDpara sa klase sa aming programa. Kung ang mga halaga ay hindi tumutugma, pagkatapos ay isang java.io. Itatapon ang InvalidClassException . Makakakita tayo ng isang halimbawa nito sa ibaba. Upang maiwasan ito, i-set lang namin nang manu-mano ang version identifier sa aming klase. Sa aming kaso, ito ay magiging katumbas lamang ng 1 (ngunit maaari mong palitan ang anumang iba pang numero na gusto mo). Well, oras na para subukang i-serialize ang aming SavedGameobject at tingnan kung ano ang mangyayari!

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

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

       // Create our object
       String[] territoryInfo = {"Spain has 6 provinces", "Russia has 10 provinces", "France has 8 provinces"};
       String[] resourcesInfo = {"Spain has 100 gold", "Russia has 80 gold", "France has 90 gold"};
       String[] diplomacyInfo = {"France is at war with Russia, Spain has taken a neutral position"};

       SavedGame savedGame = new SavedGame(territoryInfo, resourcesInfo, diplomacyInfo);

       // Create 2 streams to serialize the object and save it to a file
       FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
       ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);

       // Save the game to a file
       objectOutputStream.writeObject(savedGame);

       // Close the stream and free resources
       objectOutputStream.close();
   }
}
Gaya ng nakikita mo, gumawa kami ng 2 stream: FileOutputStreamat ObjectOutputStream. Ang una ay maaaring magsulat ng data sa isang file, at ang pangalawa ay nagko-convert ng mga bagay sa mga byte. Nakakita ka na ng mga katulad na "nested" na mga konstruksyon, halimbawa, , new BufferedReader(new InputStreamReader(...))sa mga nakaraang aralin, kaya hindi ka dapat matakot sa mga iyon :) Sa pamamagitan ng paggawa ng ganoong "chain" ng dalawang stream, ginagawa namin ang parehong mga gawain: ginagawa namin ang SavedGameobject sa isang set ng mga byte at i-save ito sa isang file gamit ang writeObject()pamamaraan. At oo nga pala, hindi man lang namin tiningnan kung ano ang nakuha namin! Oras na para tingnan ang file! *Tandaan: hindi mo kailangang gumawa ng file nang maaga. Kung walang file na may ganoong pangalan, awtomatiko itong malilikha* At narito ang mga nilalaman nito!

¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;­ТVзй{G xp t pФранция воюет СЃ Россией, Испания заняла позицию нейтралитетаuq ~ t "РЈ Испании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Испании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
Uh-oh :( Mukhang hindi gumana ang program namin :( Sa katunayan, gumana ito. Naaalala mo na nagpadala kami ng set ng mga byte, hindi lang isang bagay o text, sa file? Well, ito ang set of bytes ang hitsura :) Ito ang aming na-save na laro! Kung gusto naming ibalik ang aming orihinal na bagay, ibig sabihin, simulan at ipagpatuloy ang laro kung saan kami tumigil, kailangan namin ang reverse na proseso: deserialization . Narito kung ano ang magiging hitsura nito sa aming kaso:

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);

       SavedGame savedGame = (SavedGame) objectInputStream.readObject();

       System.out.println(savedGame);
   }
}
At narito ang resulta!

SavedGame{territoriesInfo=["Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces], resourcesInfo=[Spain has 100 gold, Russia has 80 gold, France has 90 gold], diplomacyInfo=[France is at war with Russia, Spain has taken a neutral position]}
Magaling! Nagawa muna naming i-save ang estado ng aming laro sa isang file, at pagkatapos ay i-restore ito mula sa file. Ngayon subukan nating gawin ang parehong bagay, ngunit walang identifier ng bersyon para sa aming SavedGameklase. Hindi namin muling isusulat ang aming mga klase. Ang kanilang code ay mananatiling pareho, ngunit aalisin namin private static final long serialVersionUIDsa SavedGameklase. Narito ang aming object pagkatapos ng serialization:

¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;­ТVзй{G xp t pФранция воюет СЃ Россией, Испания заняла позицию нейтралитетаuq ~ t "РЈ Испании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Испании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
Ngunit tingnan kung ano ang mangyayari kapag sinubukan naming i-deserialize ito:

InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Ito ang mismong pagbubukod na binanggit namin sa itaas. Oo nga pala, may nakalimutan kaming importante. Makatuwiran na ang Strings at primitives ay madaling mai-serialize: Ang Java ay malamang na may ilang uri ng built-in na mekanismo para gawin ito. Ngunit paano kung ang aming serializableklase ay may mga patlang na hindi primitive, ngunit sa halip ay mga sanggunian sa iba pang mga bagay? Halimbawa, gumawa tayo ng hiwalay na TerritoriesInfo, ResourcesInfoat DiplomacyInfomga klase upang gumana sa ating SavedGameklase.

public class TerritoriesInfo {
  
   private String info;

   public TerritoriesInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "TerritoriesInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}

public class ResourcesInfo {

   private String info;

   public ResourcesInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "ResourcesInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}

public class DiplomacyInfo {

   private String info;

   public DiplomacyInfo(String info) {
       this.info = info;
   }

   public String getInfo() {
       return info;
   }

   public void setInfo(String info) {
       this.info = info;
   }

   @Override
   public String toString() {
       return "DiplomacyInfo{" +
               "info='" + info + '\'' +
               '}';
   }
}
At ngayon ang isang tanong ay tumataas: kailangan ba ang lahat ng mga klase na ito Serializablekung gusto nating i-serialize ang ating binagong SavedGameklase?

import java.io.Serializable;
import java.util.Arrays;

public class SavedGame implements Serializable {

   private TerritoriesInfo territoriesInfo;
   private ResourcesInfo resourcesInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

   public TerritoriesInfo getTerritoriesInfo() {
       return territoriesInfo;
   }

   public void setTerritoriesInfo(TerritoriesInfo territoriesInfo) {
       this.territoriesInfo = territoriesInfo;
   }

   public ResourcesInfo getResourcesInfo() {
       return resourcesInfo;
   }

   public void setResourcesInfo(ResourcesInfo resourcesInfo) {
       this.resourcesInfo = resourcesInfo;
   }

   public DiplomacyInfo getDiplomacyInfo() {
       return diplomacyInfo;
   }

   public void setDiplomacyInfo(DiplomacyInfo diplomacyInfo) {
       this.diplomacyInfo = diplomacyInfo;
   }

   @Override
   public String toString() {
       return "SavedGame{" +
               "territoriesInfo=" + territoriesInfo +
               ", resourcesInfo=" + resourcesInfo +
               ", diplomacyInfo=" + diplomacyInfo +
               '}';
   }
}
Well, subukan natin ito! Iwanan natin ang lahat at subukang i-serialize ang isang SavedGamebagay:

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

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

       // Create our object
       TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
       DiplomacyInfo diplomacyInfo =  new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");


       SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);

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

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}
Resulta:

Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
Hindi ito gumana! Talaga, iyon ang sagot sa aming tanong. Kapag ang isang bagay ay na-serialize, ang lahat ng mga bagay na nire-reference ng mga instance variable nito ay serialized. At kung ang mga bagay na iyon ay tumutukoy din sa iba pang mga bagay, kung gayon ang mga ito ay serialized din. At iba pa ang ad infinitum. Ang lahat ng mga klase sa chain na ito ay dapat naSerializable , kung hindi, imposibleng i-serialize ang mga ito at isang pagbubukod ang itatapon. Sa pamamagitan ng paraan, maaari itong lumikha ng mga problema sa kalsada. Ano ang dapat nating gawin kung, halimbawa, hindi natin kailangan ng bahagi ng isang klase kapag nagse-serialize tayo? O, halimbawa, paano kung TerritoryInfodumating sa amin ang klase bilang bahagi ng ilang third-party na library. At ipagpalagay pa na hindi ito Serializableat, nang naaayon, hindi natin ito mababago. Lumalabas na hindi kami makakapagdagdag ng TerritoryInfofield sa amingSavedGameklase, dahil ang paggawa nito ay gagawing SavedGamehindi mase-serye ang buong klase! Problema iyan :/ Ano ang pagkakaiba sa pagitan ng serialization at deserialization sa Java?  - 2Sa Java, ang mga ganitong problema ay nalulutas gamit ang transientkeyword. Kung idinagdag mo ang keyword na ito sa isang field ng iyong klase, hindi ise-serialize ang field na iyon. Subukan nating gawing SavedGametransient ang isa sa mga instance field ng klase. Pagkatapos ay ise-serialize namin at i-restore ang isang bagay.

import java.io.Serializable;

public class SavedGame implements Serializable {

   private transient TerritoriesInfo territoriesInfo;
   private ResourcesInfo resourcesInfo;
   private DiplomacyInfo diplomacyInfo;

   public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
       this.territoriesInfo = territoriesInfo;
       this.resourcesInfo = resourcesInfo;
       this.diplomacyInfo = diplomacyInfo;
   }

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



import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Main {

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

       // Create our object
       TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
       ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
       DiplomacyInfo diplomacyInfo =  new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");


       SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);

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

       objectOutputStream.writeObject(savedGame);

       objectOutputStream.close();
   }
}


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);

       SavedGame savedGame = (SavedGame) objectInputStream.readObject();

       System.out.println(savedGame);

       objectInputStream.close();


   }
}
At narito ang resulta:

SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='Spain has 100 gold, Russia has 80 gold, France has 90 gold'}, diplomacyInfo=DiplomacyInfo{info='France is at war with Russia, Spain has taken a neutral position'}}
Bukod pa rito, nakakuha kami ng sagot sa aming tanong tungkol sa kung anong halaga ang itatalaga sa isang transientfield. Itatalaga ang default na halaga. Para sa mga bagay, ito ay null. Mababasa mo ang mahusay na artikulong ito sa serialization kapag mayroon kang ilang minutong natitira. Binabanggit din nito ang Externalizableinterface, na pag-uusapan natin sa susunod na aralin. Bukod pa rito, ang aklat na "Head-First Java" ay may kabanata sa paksang ito. Bigyan mo ng pansin :)
Mga komento
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION