CodeGym/Blog Java/Aleatoriu/Care este diferența dintre serializare și deserializare î...
John Squirrels
Nivel
San Francisco

Care este diferența dintre serializare și deserializare în Java?

Publicat în grup
Bună! În lecția de astăzi, vorbim despre serializare și deserializare în Java. Vom începe cu un exemplu simplu. Să presupunem că ai creat un joc pe computer. Dacă ai crescut în anii 90 și îți amintești consolele de jocuri din acea epocă, probabil știi că le lipsea ceva ce luăm de la sine înțeles astăzi — capacitatea de a salva și încărca jocuri :) Dacă nu, imaginează-ți asta! Care este diferența dintre serializare și deserializare în Java?  - 1 Mi-e teamă că astăzi un joc fără aceste abilități ar fi condamnat! Ce înseamnă oricum să „salvezi” și să „încarci” un joc? Ei bine, înțelegem sensul obișnuit: vrem să continuăm jocul din locul de unde am plecat. Pentru a face acest lucru, creăm un fel de „punct de verificare”, pe care apoi îl folosim pentru a încărca jocul. Dar ce înseamnă asta pentru un programator mai degrabă decât pentru un jucător ocazional? Răspunsul este simplu: noi'. Să presupunem că joci ca Spania în Strategium. Jocul tău are un stat: cine deține ce teritorii, cine are câte resurse, cine este într-o alianță cu cine, cine este în război cu cine și așa mai departe. Trebuie să salvăm cumva aceste informații, starea programului nostru, pentru a le restabili în viitor și a continua jocul. Căci tocmai pentru asta sunt serializarea și deserealizarea . Serializarea este procesul de stocare a stării unui obiect într-o secvență de octeți. Deserializareaeste procesul de restaurare a unui obiect din acești octeți. Orice obiect Java poate fi convertit într-o secvență de octeți. De ce am avea nevoie de asta? Am spus de mai multe ori că programele nu există de la sine. Cel mai adesea, ei interacționează cu alte programe, schimbă date etc. Și o secvență de octeți este un format convenabil și eficient. De exemplu, putem transforma SavedGameobiectul nostru într-o secvență de octeți, trimitem acești octeți prin rețea către alt computer, iar apoi pe al doilea computer transformăm acești octeți înapoi într-un obiect Java! Sună dificil, nu? Și implementarea acestui proces pare o durere :/ Din fericire, nu este așa! :) În Java,Serializableinterfața este responsabilă pentru procesul de serializare. Această interfață este extrem de simplă: nu trebuie să implementați o singură metodă pentru a o folosi! Iată cât de simplă arată clasa noastră de salvare a jocurilor:
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) +
               '}';
   }
}
Cele trei matrice sunt responsabile pentru informații despre teritorii, resurse și diplomație. Interfața Serializable îi spune mașinii virtuale Java: „ Totul este în regulă — dacă este necesar, obiectele din această clasă pot fi serializate ”. O interfață fără o singură interfață arată ciudat :/ De ce este necesară? Răspunsul la această întrebare poate fi văzut mai sus: servește doar la furnizarea informațiilor necesare mașinii virtuale Java. Într-una dintre lecțiile noastre anterioare, am menționat pe scurt interfețele de marcare . Acestea sunt interfețe informaționale speciale care pur și simplu marchează clasele noastre cu informații suplimentare care vor fi utile mașinii Java în viitor. Nu au nicio metodă pe care trebuie să le implementați.Serializableeste una dintre acele interfețe. Un alt punct important: De ce avem nevoie de private static final long serialVersionUIDvariabila pe care am definit-o în clasă? De ce este nevoie? Acest câmp conține un identificator unic pentru versiunea clasei serializate . Orice clasă care implementează Serializableinterfața are un versionidentificator. Se calculează pe baza conținutului clasei: câmpurile acesteia, ordinea în care sunt declarate, metode etc. Dacă schimbăm tipul de câmp și/sau numărul de câmpuri din clasa noastră, atunci identificatorul de versiune se schimbă imediat . serialVersionUIDeste scris și atunci când clasa este serializată. Când încercăm să deserializăm, adică să restaurăm un obiect dintr-un set de octeți, valoarea asociată serialVersionUIDeste comparată cu valoarea luiserialVersionUIDpentru clasa din programul nostru. Dacă valorile nu se potrivesc, atunci un java.io. InvalidClassException va fi aruncată. Vom vedea un exemplu în acest sens mai jos. Pentru a evita acest lucru, pur și simplu setăm manual identificatorul versiunii în clasa noastră. În cazul nostru, va fi pur și simplu egal cu 1 (dar puteți înlocui orice alt număr doriți). Ei bine, este timpul să încercăm să ne serializam SavedGameobiectul și să vedem ce se întâmplă!
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();
   }
}
După cum puteți vedea, am creat 2 fluxuri: FileOutputStreamși ObjectOutputStream. Primul poate scrie date într-un fișier, iar al doilea convertește obiectele în octeți. Ați văzut deja constructe „imbricate” similare, de exemplu, , new BufferedReader(new InputStreamReader(...))în lecțiile anterioare, așa că acestea nu ar trebui să vă sperie :) Prin crearea unui astfel de „lanț” de două fluxuri, îndeplinim ambele sarcini: transformăm obiectul SavedGameîntr-un set de octeți și salvați-l într-un fișier folosind writeObject()metoda. Și, apropo, nici nu ne-am uitat la ce am primit! E timpul să te uiți la dosar! *Notă: nu trebuie să creați fișierul în avans. Dacă un fișier cu acest nume nu există, acesta va fi creat automat* Și iată conținutul lui!
¬н 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 :( Se pare că programul nostru nu a funcționat :( De fapt, a funcționat. Vă amintiți că am trimis un set de octeți, nu doar un obiect sau text, la fișier? Ei bine, asta este ceea ce setul de octeți arată ca :) Acesta este jocul nostru salvat! Dacă dorim să restabilim obiectul original, adică să începem și să continuăm jocul de unde l-am lăsat, atunci avem nevoie de procesul invers: deserializarea . Iată cum va arăta în caz:
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);
   }
}
Și iată rezultatul!
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]}
Excelent! Am reușit să salvăm mai întâi starea jocului într-un fișier, apoi să-l restaurăm din fișier. Acum să încercăm să facem același lucru, dar fără identificatorul de versiune pentru SavedGameclasa noastră. Nu ne vom rescrie ambele clase. Codul lor va rămâne același, dar îl vom elimina private static final long serialVersionUIDdin SavedGameclasă. Iată obiectul nostru după serializare:
¬н 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 провинций
Dar uită-te la ce se întâmplă când încercăm să-l deserializăm:
InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Aceasta este însăși excepția pe care am menționat-o mai sus. Apropo, am omis ceva important. Este logic ca șirurile de caractere și primitivele să poată fi serializate cu ușurință: Java are probabil un fel de mecanism încorporat pentru a face acest lucru. Dar ce se întâmplă dacă serializableclasa noastră are câmpuri care nu sunt primitive, ci mai degrabă referințe la alte obiecte? De exemplu, să creăm clase separate TerritoriesInfoși să lucreze cu clasa noastră. ResourcesInfoDiplomacyInfoSavedGame
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 + '\'' +
               '}';
   }
}
Și acum apare o întrebare: trebuie să fie toate aceste clase Serializabledacă vrem să ne serializam SavedGameclasa modificată?
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 +
               '}';
   }
}
Ei bine, hai să-l testăm! Să lăsăm totul așa cum este și să încercăm să serializam un SavedGameobiect:
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();
   }
}
Rezultat:
Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
Nu a mers! Practic, acesta este răspunsul la întrebarea noastră. Când un obiect este serializat, toate obiectele la care se face referire de variabilele sale de instanță sunt serializate. Și dacă acele obiecte fac referire și la alte obiecte, atunci ele sunt, de asemenea, serializate. Și așa mai departe la infinit. Toate clasele din acest lanț trebuie să fieSerializable , altfel va fi imposibil să le serializeze și se va arunca o excepție. Apropo, acest lucru poate crea probleme pe viitor. Ce ar trebui să facem dacă, de exemplu, nu avem nevoie de o parte dintr-o clasă când serializăm? Sau, de exemplu, ce se întâmplă dacă TerritoryInfoclasa ar veni la noi ca parte a unei biblioteci terță parte. Și să presupunem în continuare că nu este Serializableși, în consecință, nu o putem schimba. Se pare că nu putem adăuga un TerritoryInfocâmp la noiSavedGameclasa, pentru că în acest fel ar face ca întreaga SavedGameclasă să nu fie serializabilă! Aceasta este o problemă :/ Care este diferența dintre serializare și deserializare în Java?  - 2În Java, problemele de acest fel sunt rezolvate folosind transientcuvântul cheie. Dacă adăugați acest cuvânt cheie la un câmp al clasei dvs., atunci acel câmp nu va fi serializat. Să încercăm să facem unul dintre SavedGamecâmpurile de instanță ale clasei tranzitoriu. Apoi vom serializa și vom restaura un obiect.
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();


   }
}
Și iată rezultatul:
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'}}
În plus, am primit un răspuns la întrebarea noastră despre ce valoare este atribuită unui transientcâmp. I se atribuie valoarea implicită. Pentru obiecte, aceasta este null. Puteți citi acest articol excelent despre serializare când aveți câteva minute libere. Menționează și Externalizableinterfața, despre care vom vorbi în lecția următoare. În plus, cartea „Head-First Java” are un capitol pe acest subiect. Acordă-i puțină atenție :)
Comentarii
  • Popular
  • Nou
  • Vechi
Trebuie să fii conectat pentru a lăsa un comentariu
Această pagină nu are încă niciun comentariu