CodeGym/Java-blogg/Tilfeldig/Hva er forskjellen mellom serialisering og deserialiserin...
John Squirrels
Nivå
San Francisco

Hva er forskjellen mellom serialisering og deserialisering i Java?

Publisert i gruppen
Hei! I dagens leksjon snakker vi om serialisering og deserialisering i Java. Vi starter med et enkelt eksempel. La oss si at du har laget et dataspill. Hvis du vokste opp på 90-tallet og husker spillkonsollene fra den tiden, vet du sannsynligvis at de manglet noe vi tar for gitt i dag – muligheten til å lagre og laste inn spill :) Hvis ikke, forestill deg det! Hva er forskjellen mellom serialisering og deserialisering i Java?  - 1 Jeg er redd for at i dag ville et spill uten disse evnene være dømt! Hva betyr det å "lagre" og "laste" et spill uansett? Vel, vi forstår den vanlige betydningen: vi ønsker å fortsette spillet fra stedet der vi slapp. For å gjøre dette lager vi et slags "sjekkpunkt", som vi så bruker for å laste spillet. Men hva betyr dette for en programmerer i stedet for en casual gamer? Svaret er enkelt: vi. La oss si at du spiller som Spania i Strategium. Spillet ditt har en stat: hvem eier hvilke territorier, hvem har hvor mange ressurser, hvem er i allianse med hvem, hvem er i krig med hvem, og så videre. Vi må på en eller annen måte lagre denne informasjonen, programmets tilstand, for å gjenopprette den i fremtiden og fortsette spillet. For dette er nettopp hva serialisering og deseralisering er for. Serialisering er prosessen med å lagre tilstanden til et objekt i en sekvens av byte. Deserialiseringer prosessen med å gjenopprette et objekt fra disse bytene. Ethvert Java-objekt kan konverteres til en bytesekvens. Hvorfor skulle vi trenge det? Vi har sagt mer enn en gang at programmer ikke eksisterer alene. Oftest samhandler de med andre programmer, utveksler data osv. Og en bytesekvens er et praktisk og effektivt format. For eksempel kan vi gjøre SavedGameobjektet vårt om til en sekvens av byte, sende disse bytene over nettverket til en annen datamaskin, og deretter på den andre datamaskinen gjøre disse bytene tilbake til et Java-objekt! Høres vanskelig ut, ikke sant? Og å implementere denne prosessen virker som en smerte :/ Heldigvis er det ikke slik! :) I Java, denSerializablegrensesnittet er ansvarlig for serialiseringsprosessen. Dette grensesnittet er ekstremt enkelt: du trenger ikke implementere en enkelt metode for å bruke det! Slik ser vår spillsparende klasse ut:
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 matrisene er ansvarlige for informasjon om territorier, ressurser og diplomati. Det serialiserbare grensesnittet forteller den virtuelle Java-maskinen: " Alt er OK - om nødvendig kan objekter av denne klassen serialiseres ". Et grensesnitt uten et enkelt grensesnitt ser rart ut :/ Hvorfor er det nødvendig? Svaret på dette spørsmålet kan sees ovenfor: det tjener bare til å gi nødvendig informasjon til den virtuelle Java-maskinen. I en av våre tidligere leksjoner nevnte vi kort markørgrensesnitt . Dette er spesielle informasjonsgrensesnitt som ganske enkelt markerer klassene våre med tilleggsinformasjon som vil være nyttig for Java-maskinen i fremtiden. De har ingen metoder du må implementere.Serializableer et av disse grensesnittene. Et annet viktig poeng: Hvorfor trenger vi variabelen private static final long serialVersionUIDsom vi definerte i klassen? Hvorfor trengs det? Dette feltet inneholder en unik identifikator for versjonen av den serialiserte klassen . Enhver klasse som implementerer grensesnittet Serializablehar en versionidentifikator. Den beregnes basert på innholdet i klassen: dens felter, rekkefølgen de er deklarert i, metoder osv. Hvis vi endrer felttype og/eller antall felt i klassen vår, endres versjonsidentifikatoren umiddelbart . serialVersionUIDskrives også når klassen er serialisert. Når vi prøver å deserialisere, det vil si gjenopprette et objekt fra et sett med byte, serialVersionUIDsammenlignes den assosierte med verdien avserialVersionUIDfor klassen i programmet vårt. Hvis verdiene ikke stemmer overens, så en java.io. InvalidClassException vil bli kastet. Vi vil se et eksempel på dette nedenfor. For å unngå dette setter vi ganske enkelt versjonsidentifikatoren manuelt i klassen vår. I vårt tilfelle vil det ganske enkelt være lik 1 (men du kan erstatte et hvilket som helst annet tall du vil). Vel, det er på tide å prøve å serialisere SavedGameobjektet vårt og se hva som skjer!
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 laget 2 strømmer: FileOutputStreamog ObjectOutputStream. Den første kan skrive data til en fil, og den andre konverterer objekter til byte. Du har allerede sett lignende "nestede" konstruksjoner, for eksempel , new BufferedReader(new InputStreamReader(...))i tidligere leksjoner, så de burde ikke skremme deg :) Ved å lage en slik "kjede" av to strømmer, utfører vi begge oppgavene: vi konverterer objektet SavedGametil et sett av byte og lagre den i en fil ved hjelp av writeObject()metoden. Og forresten så vi ikke engang på hva vi fikk! Det er på tide å se på filen! *Merk: du trenger ikke opprette filen på forhånd. Hvis en fil med det navnet ikke eksisterer, vil den bli opprettet automatisk* Og her er innholdet!
¬н 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 ut til at programmet vårt ikke fungerte :( Faktisk fungerte det. Du husker at vi sendte et sett med byte, ikke bare et objekt eller tekst, til filen? Vel, dette er hva det sett med byte ser ut som :) Dette er det lagrede spillet vårt! Hvis vi vil gjenopprette det opprinnelige objektet vårt, dvs. starte og fortsette spillet der vi slapp, trenger vi den omvendte prosessen: deserialisering . Slik vil det se ut i vår sak:
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]}
Utmerket! Vi klarte først å lagre spillets tilstand til en fil, og deretter gjenopprette den fra filen. La oss nå prøve å gjøre det samme, men uten versjonsidentifikatoren for SavedGameklassen vår. Vi vil ikke skrive om begge klassene våre. Koden deres forblir den samme, men vi fjerner private static final long serialVersionUIDfra SavedGameklassen. Her er objektet vårt etter 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å hva som skjer når vi prøver å deserialisere det:
InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
Dette er selve unntaket vi nevnte ovenfor. Vi gikk forresten glipp av noe viktig. Det er fornuftig at strenger og primitiver lett kan serialiseres: Java har sannsynligvis en slags innebygd mekanisme for å gjøre dette. Men hva om serializableklassen vår har felt som ikke er primitive, men heller referanser til andre objekter? La oss for eksempel lage separate TerritoriesInfo, ResourcesInfoog DiplomacyInfoklasser for å jobbe med SavedGameklassen vår.
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 nå oppstår et spørsmål: trenger alle disse klassene å være det Serializablehvis vi ønsker å serialisere vår endrede 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 +
               '}';
   }
}
Vel, la oss teste det! La oss la alt være som det er og prøve å 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 gikk ikke! I utgangspunktet er det svaret på spørsmålet vårt. Når et objekt serialiseres, serialiseres alle objektene det refereres til av forekomstvariablene. Og hvis disse objektene også refererer til andre objekter, blir de også serialisert. Og så videre i det uendelige. Alle klassene i denne kjeden må væreSerializable , ellers vil det være umulig å serialisere dem og et unntak vil bli kastet. Dette kan forresten skape problemer nedover veien. Hva skal vi gjøre hvis vi for eksempel ikke trenger en del av en klasse når vi serialiserer? Eller, for eksempel, hva om TerritoryInfoklassen kom til oss som en del av et tredjepartsbibliotek. Og anta videre at det ikke er det Serializable, og at vi følgelig ikke kan endre det. Det viser seg at vi ikke kan legge til et TerritoryInfofelt til vårSavedGameklasse, fordi det ville gjøre hele SavedGameklassen ikke-serialiserbar! Det er et problem :/ Hva er forskjellen mellom serialisering og deserialisering i Java?  - 2I Java løses problemer av denne typen ved å bruke nøkkelordet transient. Hvis du legger til dette nøkkelordet i et felt i klassen din, blir ikke det feltet serialisert. La oss prøve å gjøre et av SavedGameklassens forekomstfelt forbigående. Deretter skal vi serialisere og gjenopprette ett 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'}}
I tillegg fikk vi svar på spørsmålet vårt om hvilken verdi som blir tildelt et transientfelt. Den blir tildelt standardverdien. For objekter er dette null. Du kan lese denne utmerkede artikkelen om serialisering når du har noen minutter til overs. Den nevner også Externalizablegrensesnittet, som vi skal snakke om i neste leksjon. I tillegg har boken "Head-First Java" et kapittel om dette emnet. Gi det litt oppmerksomhet :)
Kommentarer
  • Populær
  • Ny
  • Gammel
Du må være pålogget for å legge igjen en kommentar
Denne siden har ingen kommentarer ennå