CodeGym/Java blog/Véletlen/Mi a különbség a szerializálás és a deszerializálás közöt...
John Squirrels
Szint
San Francisco

Mi a különbség a szerializálás és a deszerializálás között Java-ban?

Megjelent a csoportban
Szia! A mai leckében a Java szerializálásáról és deszerializálásáról beszélünk. Egy egyszerű példával kezdjük. Tegyük fel, hogy létrehozott egy számítógépes játékot. Ha a 90-es években nőtt fel, és emlékszik az akkori játékkonzolokra, akkor valószínűleg tudja, hogy hiányzott belőlük valami, amit ma magától értetődőnek tartunk – a játékok mentésének és betöltésének képessége :) Ha nem, képzelje el! Mi a különbség a szerializálás és a deszerializálás között Java-ban?  - 1 Félek, hogy ma egy játék ezek nélkül a képességek nélkül pusztulásra lenne ítélve! Egyébként mit jelent egy játék "mentése" és "betöltése"? Nos, értjük a hétköznapi jelentést: onnan akarjuk folytatni a játékot, ahol abbahagytuk. Ehhez létrehozunk egyfajta "ellenőrző pontot", amivel aztán betöltjük a játékot. De mit jelent ez egy programozónak, nem pedig egy hétköznapi játékosnak? A válasz egyszerű: mi. Tegyük fel, hogy Spanyolországként játszik a Strategiumban. A játékodnak van állapota: kinek melyik területe van, kinek mennyi erőforrása van, ki kivel áll szövetségben, ki kivel áll háborúban stb. Valahogy el kell mentenünk ezeket az információkat, a programunk állapotát, hogy a jövőben visszaállíthassuk és folytathassuk a játékot. Mert a szerializálás és a deserealizáció pontosan erre való. A szerializálás egy objektum állapotának bájtok sorozatában történő tárolásának folyamata. Deszerializációegy objektum visszaállításának folyamata ezekből a bájtokból. Bármely Java objektum konvertálható bájtsorozattá. Miért lenne erre szükségünk? Nem egyszer mondtuk már, hogy a programok önmagukban nem léteznek. Leggyakrabban más programokkal lépnek kapcsolatba, adatokat cserélnek stb. A bájtsorozat pedig kényelmes és hatékony formátum. SavedGamePéldául az objektumunkat bájtok sorozatává alakíthatjuk, ezeket a bájtokat a hálózaton keresztül elküldhetjük egy másik számítógépre, majd a második számítógépen ezeket a bájtokat visszafordíthatjuk Java objektummá ! Nehezen hangzik, igaz? Ennek a folyamatnak a megvalósítása pedig fájdalomnak tűnik :/ Szerencsére ez nem így van! :) Java nyelven aSerializableinterfész felelős a szerializálási folyamatért. Ez a felület rendkívül egyszerű: egyetlen módszert sem kell megvalósítania a használatához! Így néz ki egyszerűen a játékmentő osztályunk:
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) +
               '}';
   }
}
A három tömb felelős a területekkel, erőforrásokkal és a diplomáciával kapcsolatos információkért. A Serializable interfész azt mondja a Java virtuális gépnek: " Minden rendben van – ha szükséges, az osztályba tartozó objektumok sorba rendezhetők ". Furcsán néz ki egy interfész egyetlen interfész nélkül :/ Miért van rá szükség? A kérdésre a válasz fentebb látható: csak arra szolgál, hogy a Java virtuális géphez eljuttassa a szükséges információkat. Egyik korábbi leckénkben röviden megemlítettük a jelölőfelületeket . Ezek speciális információs felületek, amelyek egyszerűen megjelölik osztályainkat további információkkal, amelyek a jövőben hasznosak lesznek a Java gép számára. Nincsenek módszereik, amelyeket végre kell hajtani.Serializableaz egyik ilyen interfész. Egy másik fontos pont: Miért van szükségünk arra a private static final long serialVersionUIDváltozóra, amelyet az osztályban definiáltunk? Miért van rá szükség? Ez a mező a sorosított osztály verziójának egyedi azonosítóját tartalmazza . Minden osztály, amely megvalósítja az Serializableinterfészt, rendelkezik versionazonosítóval. Kiszámítása az osztály tartalma: mezői, deklarálási sorrendje, metódusai, stb. Ha az osztályunkban megváltoztatjuk a mező típusát és/vagy a mezők számát, akkor azonnal megváltozik a verzióazonosító . serialVersionUIDakkor is íródik, amikor az osztályt szerializálják. Amikor megpróbálunk deszerializálni, azaz visszaállítani egy objektumot egy bájtkészletből, akkor a társított serialVersionUIDérték összehasonlításra kerül aserialVersionUIDprogramunkban szereplő osztály számára. Ha az értékek nem egyeznek, akkor egy java.io. Az InvalidClassException ki lesz dobva. Az alábbiakban láthatunk erre egy példát. Ennek elkerülése érdekében egyszerűen beállítjuk manuálisan a verzióazonosítót az osztályunkban. A mi esetünkben ez egyszerűen egyenlő 1-gyel (de bármilyen más számot is helyettesíthet). Nos, itt az ideje, hogy megpróbáljuk szerializálni az objektumunkat, SavedGameés meglátjuk, mi történik!
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();
   }
}
Amint látja, 2 adatfolyamot hoztunk létre: FileOutputStreamés ObjectOutputStream. Az első képes adatokat fájlba írni, a második pedig az objektumokat bájtokká alakítja. Láttál már hasonló "beágyazott" konstrukciókat, például , new BufferedReader(new InputStreamReader(...))az előző leckéken, így ezektől nem kell megijedni :) Egy ilyen két folyamból álló "láncot" létrehozva mindkét feladatot végrehajtjuk: az objektumot SavedGamehalmazzá alakítjuk. bájtból, és mentse el egy fájlba a writeObject()metódus segítségével. És mellesleg meg sem néztük, mit kaptunk! Ideje megnézni a fájlt! *Megjegyzés: nem kell előre létrehoznia a fájlt. Ha nem létezik ilyen nevű fájl, akkor automatikusan létrejön* És itt van a tartalma!
¬н 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 провинций
Óóó :( Úgy tűnik, a programunk nem működött :( Valójában működött. Emlékszel, hogy bájtkészletet küldtünk a fájlba, nem csupán objektumot vagy szöveget? Nos, ez az, ami bájtok halmaza így néz ki :) Ez a mentett játékunk! Ha vissza akarjuk állítani az eredeti objektumunkat, azaz ott kezdjük és folytatjuk a játékot, ahol abbahagytuk, akkor fordított folyamatra van szükség: deserialization . Így fog kinézni a mi játékunkban ügy:
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);
   }
}
És itt az eredmény!
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]}
Kiváló! Sikerült először fájlba mentenünk a játékunk állapotát, majd visszaállítani a fájlból. Most próbáljuk meg ugyanezt, de az osztályunk verzióazonosítója nélkül SavedGame. Nem írjuk át mindkét osztályunkat. A kódjuk változatlan marad, de eltávolítjuk private static final long serialVersionUIDaz SavedGameosztályból. Íme az objektum a szerializálás után:
¬н 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 провинций
De nézzük meg, mi történik, amikor megpróbáljuk deszerializálni:
InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Ez a fent említett kivétel. Egyébként valami fontosat kihagytunk. Érthető, hogy a karakterláncok és a primitívek könnyen szerializálhatók: a Java valószínűleg rendelkezik valamiféle beépített mechanizmussal erre. De mi van akkor, ha az osztályunkban serializableolyan mezők vannak, amelyek nem primitívek, hanem hivatkozások más objektumokra? Például hozzunk létre különálló TerritoriesInfo, ResourcesInfoés DiplomacyInfoosztályokat az osztályunkkal való együttműködéshez SavedGame.
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 + '\'' +
               '}';
   }
}
És most felvetődik egy kérdés: ezeknek az osztályoknak kell-e lenniük, Serializableha szerializálni akarjuk a megváltozott osztályunkat SavedGame?
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 +
               '}';
   }
}
Nos, teszteljük! Hagyjunk mindent úgy, ahogy van, és próbáljunk meg szerializálni egy SavedGameobjektumot:
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();
   }
}
Eredmény:
Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
Nem sikerült! Lényegében ez a válasz a kérdésünkre. Amikor egy objektumot szerializálnak, akkor a példányváltozói által hivatkozott összes objektum sorba kerül. És ha ezek az objektumok más objektumokra is hivatkoznak, akkor szintén szerializálódnak. És így tovább a végtelenségig. Ebben a láncban az összes osztálynak .-nek kell lennieSerializable , különben lehetetlen lesz sorba rendezni őket, és kivételt kell tenni. Ez egyébként problémákat okozhat az úton. Mit tegyünk, ha például nincs szükségünk egy osztály egy részére a szerializáláskor? Vagy például mi van akkor, ha az TerritoryInfoosztály valamelyik harmadik féltől származó könyvtár részeként kerül hozzánk. És tegyük fel továbbá, hogy nem Serializable, és ennek megfelelően nem tudjuk megváltoztatni. Kiderült, hogy nem tudunk TerritoryInfomezőt hozzáadni a mieinkhezSavedGameosztályba, mert ezzel az egész SavedGameosztályt nem lehet sorosozni! Ez egy probléma :/ Mi a különbség a szerializálás és a deszerializálás között Java-ban?  - 2A Java-ban az ilyen jellegű problémákat a transientkulcsszó segítségével oldják meg. Ha hozzáadja ezt a kulcsszót az osztály egyik mezőjéhez, akkor a mező nem lesz sorosítva. Próbáljuk meg SavedGametranzienssé tenni az osztály egyik példánymezőjét. Ezután sorosítunk és visszaállítunk egy objektumot.
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();


   }
}
És íme az eredmény:
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'}}
Ezenkívül választ kaptunk arra a kérdésünkre, hogy milyen értéket rendelnek egy mezőhöz transient. Az alapértelmezett értéket kapja. Az objektumok esetében ez a null. Ha van néhány szabad perce, elolvashatja ezt a kiváló cikket a sorozatkészítésről . Megemlíti a Externalizablefelületet is, amelyről a következő leckében fogunk beszélni. Ezenkívül a „Head-First Java” című könyvnek van egy fejezete erről a témáról. Figyelj rá egy kicsit :)
Hozzászólások
  • Népszerű
  • Új
  • Régi
Hozzászólás írásához be kell jelentkeznie
Ennek az oldalnak még nincsenek megjegyzései