CodeGym/Java blog/Tilfældig/Hvad er forskellen mellem serialisering og deserialiserin...
John Squirrels
Niveau
San Francisco

Hvad er forskellen mellem serialisering og deserialisering i Java?

Udgivet i gruppen
Hej! I dagens lektion taler vi om serialisering og deserialisering i Java. Vi starter med et simpelt eksempel. Lad os sige, at du har lavet et computerspil. Hvis du voksede op i 90'erne og husker den tids spilkonsoller, ved du sikkert, at de manglede noget, vi tager for givet i dag - evnen til at gemme og indlæse spil :) Hvis ikke, så forestil dig det! Hvad er forskellen mellem serialisering og deserialisering i Java?  - 1 Jeg er bange for, at et spil uden disse evner i dag ville være dømt! Hvad betyder det alligevel at "gemme" og "indlæse" et spil? Nå, vi forstår den almindelige betydning: vi vil fortsætte spillet fra det sted, hvor vi slap. For at gøre dette opretter vi en slags "check point", som vi så bruger til at indlæse spillet. Men hvad betyder det for en programmør i stedet for en casual gamer? Svaret er enkelt: vi. Lad os sige, at du spiller som Spanien i Strategium. Dit spil har en stat: hvem ejer hvilke territorier, hvem har hvor mange ressourcer, hvem er i en alliance med hvem, hvem er i krig med hvem, og så videre. Vi skal på en eller anden måde gemme denne information, vores programs tilstand, for at gendanne den i fremtiden og fortsætte spillet. For det er netop, hvad serialisering og deseralisering er til for. Serialisering er processen med at lagre et objekts tilstand i en sekvens af bytes. Deserialiseringer processen med at gendanne et objekt fra disse bytes. Ethvert Java-objekt kan konverteres til en byte-sekvens. Hvorfor skulle vi have brug for det? Vi har sagt mere end én gang, at programmer ikke eksisterer alene. Oftest interagerer de med andre programmer, udveksler data osv. Og en bytesekvens er et bekvemt og effektivt format. For eksempel kan vi omdanne vores SavedGameobjekt til en sekvens af bytes, sende disse bytes over netværket til en anden computer, og så på den anden computer omdanne disse bytes tilbage til et Java-objekt! Det lyder svært, ikke? Og at implementere denne proces virker som en smerte :/ Heldigvis er det ikke sådan! :) I Java, denSerializableinterface er ansvarlig for serialiseringsprocessen. Denne grænseflade er ekstremt enkel: du behøver ikke implementere en enkelt metode for at bruge den! Sådan ser vores spilbesparende klasse ud:
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) +
               '}';
   }
}
De tre arrays er ansvarlige for information om territorier, ressourcer og diplomati. Grænsefladen, der kan serialiseres, fortæller den virtuelle Java-maskine: " Alt er OK - om nødvendigt kan objekter af denne klasse serialiseres ". En grænseflade uden en enkelt grænseflade ser mærkelig ud :/ Hvorfor er det nødvendigt? Svaret på dette spørgsmål kan ses ovenfor: det tjener kun til at give den nødvendige information til den virtuelle Java-maskine. I en af ​​vores tidligere lektioner nævnte vi kort markørgrænseflader . Disse er specielle informationsgrænseflader, der blot markerer vores klasser med yderligere information, som vil være nyttig for Java-maskinen i fremtiden. De har ingen metoder, du skal implementere.Serializableer en af ​​disse grænseflader. En anden vigtig pointe: Hvorfor har vi brug for den private static final long serialVersionUIDvariabel, som vi definerede i klassen? Hvorfor er det nødvendigt? Dette felt indeholder en unik identifikator for versionen af ​​den serialiserede klasse . Enhver klasse, der implementerer Serializablegrænsefladen, har en versionidentifikator. Det beregnes ud fra indholdet af klassen: dens felter, rækkefølgen de er deklareret i, metoder osv. Hvis vi ændrer felttypen og/eller antallet af felter i vores klasse, så ændres versionsidentifikatoren med det samme . serialVersionUIDskrives også når klassen serialiseres. Når vi forsøger at deserialisere, det vil sige at gendanne et objekt fra et sæt bytes, serialVersionUIDsammenlignes det tilknyttede med værdien afserialVersionUIDfor klassen i vores program. Hvis værdierne ikke stemmer overens, så en java.io. InvalidClassException vil blive kastet. Vi vil se et eksempel på dette nedenfor. For at undgå dette sætter vi blot versionsidentifikatoren manuelt i vores klasse. I vores tilfælde vil det simpelthen være lig med 1 (men du kan erstatte et hvilket som helst andet tal, du vil). Nå, det er tid til at prøve at serialisere vores SavedGameobjekt og se, hvad der sker!
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();
   }
}
Som du kan se, har vi oprettet 2 streams: FileOutputStreamog ObjectOutputStream. Den første kan skrive data til en fil, og den anden konverterer objekter til bytes. Du har allerede set lignende "indlejrede" konstruktioner, for eksempel , new BufferedReader(new InputStreamReader(...))i tidligere lektioner, så de burde ikke skræmme dig :) Ved at skabe sådan en "kæde" af to strømme udfører vi begge opgaver: vi konverterer objektet SavedGametil et sæt af bytes og gem det i en fil ved hjælp af writeObject()metoden. Og i øvrigt så vi ikke engang på, hvad vi fik! Det er tid til at se på filen! *Bemærk: du behøver ikke oprette filen på forhånd. Hvis en fil med det navn ikke findes, oprettes den automatisk* Og her er dens indhold!
¬н 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 :( Det ser ud til, at vores program ikke virkede :( Faktisk virkede det. Du kan huske, at vi sendte et sæt bytes, ikke blot et objekt eller tekst, til filen? Nå, det er hvad der sæt bytes ser ud :) Dette er vores gemte spil! Hvis vi vil gendanne vores oprindelige objekt, dvs. starte og fortsætte spillet, hvor vi slap, så har vi brug for den omvendte proces: deserialisering. Sådan ser det ud i vores sag:
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);
   }
}
Og her er resultatet!
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]}
Fremragende! Vi formåede først at gemme vores spils tilstand til en fil og derefter gendanne den fra filen. Lad os nu prøve at gøre det samme, men uden versions-id'et for vores SavedGameklasse. Vi vil ikke omskrive begge vores klasser. Deres kode forbliver den samme, men vi fjerner private static final long serialVersionUIDfra SavedGameklassen. Her er vores objekt efter serialisering:
¬н 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 провинций
Men se på, hvad der sker, når vi forsøger at deserialisere det:
InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Dette er selve undtagelsen, vi nævnte ovenfor. I øvrigt gik vi glip af noget vigtigt. Det giver mening, at strenge og primitiver let kan serialiseres: Java har sandsynligvis en form for indbygget mekanisme til at gøre dette. Men hvad nu hvis vores serializableklasse har felter, der ikke er primitive, men derimod referencer til andre objekter? Lad os f.eks. oprette separate TerritoriesInfo, ResourcesInfoog DiplomacyInfoklasser til at arbejde med vores SavedGameklasse.
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 + '\'' +
               '}';
   }
}
Og nu rejser et spørgsmål sig: skal alle disse klasser være det, Serializablehvis vi vil serialisere vores ændrede SavedGameklasse?
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 +
               '}';
   }
}
Nå, lad os teste det! Lad os lade alt være som det er og prøve at serialisere et SavedGameobjekt:
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();
   }
}
Resultat:
Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
Det virkede ikke! Det er i bund og grund svaret på vores spørgsmål. Når et objekt serialiseres, serialiseres alle de objekter, der refereres til af dets instansvariabler. Og hvis disse objekter også refererer til andre objekter, bliver de også serialiseret. Og så videre i det uendelige. Alle klasser i denne kæde skal væreSerializable , ellers vil det være umuligt at serialisere dem, og en undtagelse vil blive kastet. Det kan i øvrigt skabe problemer hen ad vejen. Hvad skal vi gøre, hvis vi for eksempel ikke har brug for en del af en klasse, når vi serialiserer? Eller for eksempel, hvad hvis TerritoryInfoklassen kom til os som en del af et tredjepartsbibliotek. Og antag yderligere, at det ikke er det Serializable, og at vi derfor ikke kan ændre det. Det viser sig, at vi ikke kan tilføje et TerritoryInfofelt til voresSavedGameklasse, fordi det ville gøre hele SavedGameklassen ikke-serialiserbar! Det er et problem :/ Hvad er forskellen mellem serialisering og deserialisering i Java?  - 2I Java løses problemer af denne art ved hjælp af nøgleordet transient. Hvis du føjer dette søgeord til et felt i din klasse, bliver dette felt ikke serialiseret. Lad os prøve at gøre et af SavedGameklassens instansfelter forbigående. Derefter vil vi serialisere og gendanne ét objekt.
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();


   }
}
Og her er resultatet:
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'}}
Derudover fik vi svar på vores spørgsmål om, hvilken værdi der tildeles et transientfelt. Den får tildelt standardværdien. For genstande er dette null. Du kan læse denne fremragende artikel om serialisering, når du har et par minutter til overs. Det nævner også Externalizablegrænsefladen, som vi vil tale om i næste lektion. Derudover har bogen "Head-First Java" et kapitel om dette emne. Giv det lidt opmærksomhed :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du skal være logget ind for at skrive en kommentar
Denne side har ingen kommentarer endnu