Programmering

Java-serialiseringsalgoritmen afsløret

Serialisering er processen med at gemme et objekts tilstand i en række af bytes; deserialisering er processen med at genopbygge disse bytes til et levende objekt. Java Serialization API giver en standardmekanisme for udviklere til at håndtere objektserialisering. I dette tip vil du se, hvordan du serieliserer et objekt, og hvorfor serialisering undertiden er nødvendig. Du lærer om serialiseringsalgoritmen, der bruges i Java, og ser et eksempel, der illustrerer det serielle format på et objekt. Når du er færdig, skal du have et solidt kendskab til, hvordan serialiseringsalgoritmen fungerer, og hvilke enheder der serieliseres som en del af objektet på et lavt niveau.

Hvorfor kræves serialisering?

I dagens verden vil en typisk virksomhedsapplikation have flere komponenter og distribueres på forskellige systemer og netværk. I Java er alt repræsenteret som objekter; hvis to Java-komponenter vil kommunikere med hinanden, skal der være en mekanisme til udveksling af data. En måde at opnå dette på er at definere din egen protokol og overføre et objekt. Dette betyder, at den modtagende ende skal kende den protokol, som afsenderen bruger til at genskabe objektet, hvilket vil gøre det meget vanskeligt at tale med tredjepartskomponenter. Derfor skal der være en generisk og effektiv protokol for at overføre objektet mellem komponenter. Serialisering er defineret til dette formål, og Java-komponenter bruger denne protokol til at overføre objekter.

Figur 1 viser et højt niveau af klient / server kommunikation, hvor et objekt overføres fra klienten til serveren gennem serialisering.

Figur 1. Et højt niveau af serialisering i aktion (klik for at forstørre)

Sådan serialiseres et objekt

For at serieisere et objekt skal du sikre dig, at objektets klasse implementerer java.io.Serialiserbar interface, som vist i liste 1.

Notering 1. Implementering Serializable

 import java.io.Serializable; klasse TestSerial implementerer Serialiserbar {public byte version = 100; antal offentlige byte = 0; } 

I liste 1 er det eneste, du skulle gøre anderledes end at oprette en normal klasse, at implementere java.io.Serialiserbar interface. Det Serialiserbar interface er en markørgrænseflade; det erklærer slet ingen metoder. Det fortæller serialiseringsmekanismen, at klassen kan serieiseres.

Nu hvor du har gjort klassen kvalificeret til serialisering, er det næste trin at faktisk serieisere objektet. Det gøres ved at kalde writeObject () metode til java.io.ObjectOutputStream klasse, som vist i liste 2.

Liste 2. Opkald til writeObject ()

 offentlig statisk ugyldig hoved (String args []) kaster IOException {FileOutputStream fos = ny FileOutputStream ("temp.out"); ObjectOutputStream oos = ny ObjectOutputStream (fos); TestSerial ts = ny TestSerial (); oos.writeObject (ts); oos.flush (); oos.close (); } 

På liste 2 gemmes tilstanden for TestSerie objekt i en fil, der hedder temp. ud. oos.writeObject (ts); starter faktisk serialiseringsalgoritmen, som igen skriver objektet til temp. ud.

For at genskabe objektet fra den vedvarende fil, anvender du koden i liste 3.

Fortegnelse 3. Gendannelse af et serieobjekt

 offentlig statisk ugyldig hoved (String args []) kaster IOException {FileInputStream fis = ny FileInputStream ("temp.out"); ObjectInputStream oin = ny ObjectInputStream (fis); TestSerial ts = (TestSerial) oin.readObject (); System.out.println ("version =" + ts.version); } 

I Listing 3 finder objektets gendannelse sted med oin.readObject () metodeopkald. Denne metodeopkald læser de rå bytes, som vi tidligere har opretholdt, og opretter et levende objekt, der er en nøjagtig replika af den oprindelige objektgraf. Fordi readObject () kan læse ethvert serie, der kan serialiseres, kræves en rollebesætning af den rigtige type.

Udførelse af denne kode udskrives version = 100 på standardoutput.

Seriens format på et objekt

Hvordan ser den serielle version af objektet ud? Husk, at prøvekoden i det forrige afsnit gemte den serielle version af TestSerie objekt i filen temp. ud. Liste 4 viser indholdet af temp. ud, vises i hexadecimal. (Du har brug for en hexadecimal editor for at se output i hexadecimalt format.)

Fortegnelse 4. Hexadecimal form for TestSerial

 AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65 73 74 A0 0C 34 00 FE B1 DD F9 02 00 02 42 00 05 63 6F 75 6E 74 42 00 07 76 65 72 73 69 6F 6E 78 70 00 64 

Hvis du ser igen på det faktiske TestSerie objekt, vil du se, at det kun har to byte-medlemmer, som vist i liste 5.

Notering 5. TestSerials byte-medlemmer

 public byte version = 100; antal offentlige byte = 0; 

Størrelsen på en bytevariabel er en byte, og derfor er objektets samlede størrelse (uden overskrift) to byte. Men hvis du ser på størrelsen på det serielle objekt i Listing 4, vil du se 51 byte. Overraskelse! Hvor kom de ekstra byte fra, og hvad er deres betydning? De introduceres af serialiseringsalgoritmen og er nødvendige for at genskabe genstanden. I det næste afsnit udforsker du denne algoritme i detaljer.

Java's serialiseringsalgoritme

Nu skal du have et ret godt kendskab til, hvordan du serieliserer et objekt. Men hvordan fungerer processen under emhætten? Generelt gør serialiseringsalgoritmen følgende:

  • Det skriver metadata for den klasse, der er knyttet til en forekomst.
  • Den skriver rekursivt beskrivelsen af ​​superklassen ud, indtil den finder java.lang.objekt.
  • Når den er færdig med at skrive metadataoplysningerne, starter den med de faktiske data, der er knyttet til forekomsten. Men denne gang starter det fra den øverste superklasse.
  • Det skriver rekursivt de data, der er knyttet til forekomsten, startende fra den mindst superklasse til den mest afledte klasse.

Jeg har skrevet et andet eksempel på et objekt til dette afsnit, der dækker alle mulige tilfælde. Det nye prøveobjekt, der skal serieliseres, vises i liste 6.

Liste 6. Eksempel på serieobjekt

 klasseforældreredskaber Serialiserbar {int parentVersion = 10; } klasse indeholder redskaber Serialiserbare {int containVersion = 11; } offentlig klasse SerialTest udvider overordnede redskaber Serialiserbar {int version = 66; indeholder con = nyt contain (); public int getVersion () {return version; } offentlig statisk ugyldig hoved (String args []) kaster IOException {FileOutputStream fos = ny FileOutputStream ("temp.out"); ObjectOutputStream oos = ny ObjectOutputStream (fos); SerialTest st = ny SerialTest (); oos.writeObject (st); oos.flush (); oos.close (); }} 

Dette eksempel er ligetil. Den serieliserer et objekt af typen SerialTest, som er afledt af forælder og har en containerobjekt, indeholde. Det serielle format for dette objekt vises i liste 7.

Liste 7. Serialiseret form af prøveobjekt

 AC ED 00 05 73 72 00 0A 53 65 72 69 61 6C 54 65 73 74 05 52 81 5A AC 66 02 F6 02 00 02 49 00 07 76 65 72 73 69 6F 6E 4C 00 03 63 6F 6E 74 00 09 4C 63 6F 6E 74 61 69 6E 3B 78 72 00 06 70 61 72 65 6E 74 0E DB D2 BD 85 EE 63 7A 02 00 01 49 00 0D 70 61 72 65 6E 74 56 65 72 73 69 6F 6E 78 70 00 00 0A 00 00 00 42 73 72 00 07 63 6F 6E 74 61 69 6E FC BB E6 0E FB CB 60 C7 02 00 01 49 00 0E 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E 78 70 00 00 00 0B 

Figur 2 tilbyder et højt kig på serialiseringsalgoritmen for dette scenarie.

Figur 2. En oversigt over serialiseringsalgoritmen

Lad os gennemgå det serielle format på objektet og se, hvad hver byte repræsenterer. Begynd med oplysningerne om serieprotokol:

  • AC ED: STREAM_MAGIC. Angiver, at dette er en serialiseringsprotokol.
  • 00 05: STREAM_VERSION. Serialiseringsversionen.
  • 0x73: TC_OBJECT. Angiver, at dette er et nyt Objekt.

Det første trin i serialiseringsalgoritmen er at skrive beskrivelsen af ​​den klasse, der er knyttet til en forekomst. Eksemplet serialiserer et objekt af typen SerialTest, så algoritmen starter med at skrive beskrivelsen af SerialTest klasse.

  • 0x72: TC_CLASSDESC. Angiver, at dette er en ny klasse.
  • 00 0A: Klassenavnets længde.
  • 53 65 72 69 61 6c 54 65 73 74: SerialTest, navnet på klassen.
  • 05 52 81 5A AC 66 02 F6: SerialVersionUID, serienummeridentifikatoren for denne klasse.
  • 0x02: Forskellige flag. Dette særlige flag siger, at objektet understøtter serialisering.
  • 00 02: Antal felter i denne klasse.

Derefter skriver algoritmen feltet int version = 66;.

  • 0x49: Felttypekode. 49 repræsenterer "jeg", som står for Int.
  • 00 07: Længden af ​​feltnavnet.
  • 76 65 72 73 69 6F 6E: version, navnet på feltet.

Og så skriver algoritmen det næste felt, indeholder con = nyt contain ();. Dette er et objekt, så det skriver den kanoniske JVM-signatur i dette felt.

  • 0x74: TC_STRING. Repræsenterer en ny streng.
  • 00 09: Strengens længde.
  • 4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, den kanoniske JVM-signatur.
  • 0x78: TC_ENDBLOCKDATA, slutningen af ​​de valgfri blokdata for et objekt.

Det næste trin i algoritmen er at skrive beskrivelsen af forælder klasse, som er den umiddelbare superklasse af SerialTest.

  • 0x72: TC_CLASSDESC. Angiver, at dette er en ny klasse.
  • 00 06: Klassenavnets længde.
  • 70 61 72 65 6E 74: SerialTest, navnet på klassen
  • 0E DB D2 BD 85 EE 63 7A: SerialVersionUID, serienummeridentifikatoren for denne klasse.
  • 0x02: Forskellige flag. Dette flag bemærker, at objektet understøtter serialisering.
  • 00 01: Antal felter i denne klasse.

Nu skriver algoritmen feltbeskrivelsen for forælder klasse. forælder har et felt, int parentVersion = 100;.

  • 0x49: Felttypekode. 49 repræsenterer "jeg", som står for Int.
  • 00 0D: Længden af ​​feltnavnet.
  • 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: forældreversion, navnet på feltet.
  • 0x78: TC_ENDBLOCKDATA, slutningen af ​​blokdata for dette objekt.
  • 0x70: TC_NULL, hvilket repræsenterer det faktum, at der ikke er flere superklasser, fordi vi har nået toppen af ​​klassehierarkiet.

Indtil videre har serialiseringsalgoritmen skrevet beskrivelsen af ​​klassen, der er knyttet til forekomsten og alle dens superklasser. Dernæst skriver den de faktiske data, der er knyttet til forekomsten. Det skriver forældrenes klassemedlemmer først:

  • 00 00 00 0A: 10, værdien af forældreversion.

Så går det videre til SerialTest.

  • 00 00 00 42: 66, værdien af version.

De næste par byte er interessante. Algoritmen skal skrive oplysningerne om indeholde objekt, vist i liste 8.

Liste 8. Indeholder objektet

 indeholder con = nyt contain (); 

Husk, at serialiseringsalgoritmen ikke har skrevet klassebeskrivelsen til indeholde klasse endnu. Dette er muligheden for at skrive denne beskrivelse.

  • 0x73: TC_OBJECT, betegner et nyt objekt.
  • 0x72: TC_CLASSDESC.
  • 00 07: Klassenavnets længde.
  • 63 6F 6E 74 61 69 6E: indeholde, navnet på klassen.
  • FC BB E6 0E FB CB 60 C7: SerialVersionUID, serienummeridentifikatoren for denne klasse.
  • 0x02: Forskellige flag. Dette flag angiver, at denne klasse understøtter serialisering.
  • 00 01: Antal felter i denne klasse.

Derefter skal algoritmen skrive beskrivelsen for indeholde's eneste felt, int containVersion = 11;.

  • 0x49: Felttypekode. 49 repræsenterer "jeg", som står for Int.
  • 00 0E: Længden af ​​feltnavnet.
  • 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, navnet på feltet.
  • 0x78: TC_ENDBLOCKDATA.

Derefter kontrollerer serialiseringsalgoritmen for at se, om indeholde har nogen overordnede klasser. Hvis det gjorde det, ville algoritmen begynde at skrive den klasse; men i dette tilfælde er der ingen superklasse til indeholde, så algoritmen skriver TC_NULL.

  • 0x70: TC_NULL.

Endelig skriver algoritmen de faktiske data, der er knyttet til indeholde.

  • 00 00 00 0B: 11, værdien af containVersion.

Konklusion

I dette tip har du set, hvordan du serieliserer et objekt, og har lært, hvordan serialiseringsalgoritmen fungerer i detaljer. Jeg håber, at denne artikel giver dig flere detaljer om, hvad der sker, når du faktisk serieliserer et objekt.

Om forfatteren

Sathiskumar Palaniappan har mere end fire års erfaring inden for it-branchen og har arbejdet med Java-relaterede teknologier i mere end tre år. I øjeblikket arbejder han som systemsoftwareingeniør ved Java Technology Center, IBM Labs. Han har også erfaring inden for telekombranchen.

Ressourcer

  • Læs specifikationerne for Java-objektserialisering. (Spec er en PDF.)
  • "Flad dine objekter: Opdag hemmelighederne ved Java Serialization API" (Todd M. Greanier, JavaWorld, juli 2000) giver et kig ind i møtrikkerne og skruerne i serialiseringsprocessen.
  • Kapitel 10 i Java RMI (William Grosso, O'Reilly, oktober 2001) er også en nyttig reference.

Denne historie, "Java-serialiseringsalgoritmen afsløret" blev oprindeligt udgivet af JavaWorld.