Programovanie

Bol odhalený algoritmus serializácie Java

Serializácia je proces ukladania stavu objektu do postupnosti bajtov; deserializácia je proces prebudovania týchto bajtov na živý objekt. Rozhranie Java Serialization API poskytuje vývojárom štandardný mechanizmus na spracovanie serializácie objektov. V tomto tipe uvidíte, ako serializovať objekt a prečo je niekedy potrebná serializácia. Dozviete sa o algoritme serializácie použitom v prostredí Java a uvidíte príklad, ktorý ilustruje serializovaný formát objektu. Keď skončíte, mali by ste mať dôkladné vedomosti o tom, ako funguje algoritmus serializácie a aké entity sú serializované ako súčasť objektu na nízkej úrovni.

Prečo je vyžadovaná serializácia?

V dnešnom svete bude typická podniková aplikácia obsahovať viac komponentov a bude sa distribuovať do rôznych systémov a sietí. V Jave je všetko reprezentované ako objekty; ak chcú dve komponenty Java navzájom komunikovať, musí existovať mechanizmus na výmenu údajov. Jedným zo spôsobov, ako to dosiahnuť, je definovanie vlastného protokolu a prenos objektu. To znamená, že prijímajúci koniec musí poznať protokol použitý odosielateľom na opätovné vytvorenie objektu, čo by veľmi sťažilo komunikáciu s komponentmi tretích strán. Preto musí existovať všeobecný a efektívny protokol na prenos objektu medzi komponentmi. Na tento účel je definovaná serializácia a komponenty Java používajú tento protokol na prenos objektov.

Obrázok 1 zobrazuje pohľad na vysokej úrovni na komunikáciu klient / server, keď sa objekt prenáša z klienta na server prostredníctvom serializácie.

Obrázok 1. Vysokoúrovňový pohľad na serializáciu v akcii (kliknutím ju zväčšite)

Ako serializovať objekt

Ak chcete serializovať objekt, musíte sa ubezpečiť, že trieda objektu implementuje java.io. Serializovateľné ako je uvedené v zozname 1.

Zoznam 1. Implementácia Serializovateľného

 import java.io.Serializovateľný; trieda TestSerial implementuje Serializable {public byte version = 100; počet verejných bajtov = 0; } 

V zozname 1 je jediná vec, ktorú ste museli urobiť inak ako pri vytváraní normálnej triedy, implementácia java.io. Serializovateľné rozhranie. The Serializovateľné interface je markerové rozhranie; nedefinuje vôbec žiadne metódy. Povie mechanizmu serializácie, že triedu je možné serializovať.

Teraz, keď ste triedu prispôsobili na serializáciu, ďalším krokom je skutočná serializácia objektu. To sa deje volaním writeObject () metóda java.io.ObjectOutputStream triedy, ako je uvedené v zozname 2.

Zoznam 2. Volanie writeObject ()

 public static void main (String args []) hodí IOException {FileOutputStream fos = nový FileOutputStream ("temp.out"); ObjectOutputStream oos = nový ObjectOutputStream (fos); TestSerial ts = nový TestSerial (); oos.writeObject (ts); oos.flush (); oos.close (); } 

Zoznam 2 ukladá stav súboru TestSerial objekt v súbore s názvom temp.out. oos.writeObject (ts); vlastne odštartuje serializačný algoritmus, ktorý následne zapíše objekt do temp.out.

Na opätovné vytvorenie objektu z trvalého súboru by ste použili kód uvedený v zozname 3.

Zoznam 3. Opätovné vytvorenie serializovaného objektu

 public static void main (String args []) vyvolá IOException {FileInputStream fis = nový FileInputStream ("temp.out"); ObjectInputStream oin = nový ObjectInputStream (fis); TestSerial ts = (TestSerial) oin.readObject (); System.out.println ("verzia =" + ts.verzia); } 

V zozname 3 sa obnova objektu uskutoční pomocou oin.readObject () volanie metódy. Toto volanie metódy číta v surových bytoch, ktoré sme predtým pretrvávali, a vytvára živý objekt, ktorý je presnou replikou grafu pôvodného objektu. Pretože readObject () dokáže čítať akýkoľvek serializovateľný objekt, je potrebné obsadenie správneho typu.

Spustením tohto kódu sa vytlačí verzia = 100 na štandardný výstup.

Serializovaný formát objektu

Ako vyzerá serializovaná verzia objektu? Pamätajte, že vzorový kód v predchádzajúcej časti uložil serializovanú verziu servera TestSerial objekt do súboru temp.out. Výpis 4 zobrazuje obsah temp.out, zobrazené v šestnástkovej sústave. (Potrebujete hexadecimálny editor, aby ste videli výstup v hexadecimálnom formáte.)

Výpis 4. Šestnástková forma 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 

Ak sa pozriete znova na skutočné TestSerial uvidíte, že má iba dvoch bajtových členov, ako je uvedené v zozname 5.

Zoznam 5. Členovia bajtov TestSerial

 verzia verejného bajtu = 100; počet verejných bajtov = 0; 

Veľkosť bajtovej premennej je jeden bajt, a teda celková veľkosť objektu (bez hlavičky) sú dva bajty. Ak sa ale pozriete na veľkosť serializovaného objektu v zozname 4, uvidíte 51 bajtov. Prekvapenie! Odkiaľ sa zobrali ďalšie bajty a aký je ich význam? Sú zavedené algoritmom serializácie a sú potrebné na opätovné vytvorenie objektu. V nasledujúcej časti podrobne preskúmame tento algoritmus.

Algoritmus serializácie Java

Teraz by ste už mali mať dosť dobré vedomosti o tom, ako serializovať objekt. Ako však proces prebieha pod kapotou? Algoritmus serializácie všeobecne robí toto:

  • Vypíše metadáta triedy spojené s inštanciou.
  • Rekurzívne zapisuje popis nadtriedy, kým nenájde java.lang.objekt.
  • Po dokončení zápisu informácií o metadátach sa začne so skutočnými údajmi spojenými s inštanciou. Tentokrát to však začína od najvyššej nadtriedy.
  • Rekurzívne zapisuje údaje spojené s inštanciou, počnúc od najmenej nadtriedy po najviac odvodenú triedu.

Pre túto časť som napísal iný vzorový objekt, ktorý sa bude zaoberať všetkými možnými prípadmi. Nový vzorový objekt, ktorý sa má serializovať, je uvedený v zozname 6.

Výpis 6. Vzorový serializovaný objekt

 trieda rodič implementuje Serializable {int parentVersion = 10; } trieda obsahuje implementácie Serializable {int containVersion = 11; } public class SerialTest extends parent implements Serializable {int version = 66; contain con = new contain (); public int getVersion () {návratová verzia; } public static void main (String args []) hodí IOException {FileOutputStream fos = nový FileOutputStream ("temp.out"); ObjectOutputStream oos = nový ObjectOutputStream (fos); SerialTest st = nový SerialTest (); oos.writeObject (st); oos.flush (); oos.close (); }} 

Tento príklad je priamy. Serializuje objekt typu SerialTest, ktorý je odvodený z rodič a má kontajnerový objekt, obsahovať. Serializovaný formát tohto objektu je uvedený v zozname 7.

Výpis 7. Serializovaná forma vzorového objektu

 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 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 

Obrázok 2 ponúka na vysokej úrovni pohľad na algoritmus serializácie pre tento scenár.

Obrázok 2. Náčrt algoritmu serializácie

Prejdime si podrobne serializovaný formát objektu a pozrime sa, čo ktorý bajt predstavuje. Začnite informáciami o serializačnom protokole:

  • AC ED: STREAM_MAGIC. Určuje, že sa jedná o protokol serializácie.
  • 00 05: STREAM_VERSION. Verzia na serializáciu.
  • 0x73: TC_OBJECT. Určuje, že ide o nový Objekt.

Prvým krokom algoritmu serializácie je napísanie popisu triedy spojenej s inštanciou. Príklad serializuje objekt typu SerialTest, takže algoritmus začína napísaním popisu súboru SerialTest trieda.

  • 0x72: TC_CLASSDESC. Určuje, že sa jedná o novú triedu.
  • 00 0A: Dĺžka názvu triedy.
  • 53 65 72 69 61 6c 54 65 73 74: SerialTest, názov triedy.
  • 05 52 81 5A AC 66 02 F6: SerialVersionUID, sériový identifikátor verzie tejto triedy.
  • 0x02: Rôzne vlajky. Tento konkrétny príznak hovorí, že objekt podporuje serializáciu.
  • 00 02: Počet polí v tejto triede.

Ďalej algoritmus zapíše pole int verzia = 66;.

  • 0x49: Kód typu poľa. 49 predstavuje „I“, čo znamená Int.
  • 00 07: Dĺžka názvu poľa.
  • 76 65 72 73 69 6F 6E: verzia, názov poľa.

A potom algoritmus napíše ďalšie pole, contain con = new contain ();. Toto je objekt, takže zapíše kanonický podpis JVM tohto poľa.

  • 0x74: TC_STRING. Predstavuje nový reťazec.
  • 00 09: Dĺžka šnúrky.
  • 4C 63 6F 6E 74 61 69 6E 3B: Lobsah;, kanonický podpis JVM.
  • 0x78: TC_ENDBLOCKDATA, koniec voliteľných údajov bloku pre objekt.

Ďalším krokom algoritmu je napísanie popisu súboru rodič trieda, ktorá je bezprostrednou nadtriedou triedy SerialTest.

  • 0x72: TC_CLASSDESC. Určuje, že sa jedná o novú triedu.
  • 00 06: Dĺžka názvu triedy.
  • 70 61 72 65 6E 74: SerialTest, názov triedy
  • 0E DB D2 BD 85 EE 63 7A: SerialVersionUID, sériový identifikátor verzie tejto triedy.
  • 0x02: Rôzne vlajky. Tento príznak označuje, že objekt podporuje serializáciu.
  • 00 01: Počet polí v tejto triede.

Teraz algoritmus napíše popis poľa pre rodič trieda. rodič má jedno pole, int parentVersion = 100;.

  • 0x49: Kód typu poľa. 49 predstavuje „I“, čo znamená Int.
  • 00 0D: Dĺžka názvu poľa.
  • 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion, názov poľa.
  • 0x78: TC_ENDBLOCKDATA, koniec dát bloku pre tento objekt.
  • 0x70: TC_NULL, čo predstavuje skutočnosť, že už neexistujú žiadne nadtriedy, pretože sme sa dostali na vrchol hierarchie tried.

Alergický algoritmus doteraz napísal popis triedy spojenej s inštanciou a všetky jej nadtriedy. Ďalej zapíše skutočné údaje spojené s inštanciou. Najskôr napíše členov nadradenej triedy:

  • 00 00 00 0A: 10, hodnota parentVersion.

Potom sa presunie na SerialTest.

  • 00 00 00 42: 66, hodnota verzia.

Nasledujúcich niekoľko bajtov je zaujímavých. Algoritmus musí zapisovať informácie o obsahovať objekt uvedený v zozname 8.

Výpis 8. Objekt contain

 contain con = new contain (); 

Pamätajte, že algoritmus na serializáciu nenapísal popis triedy pre obsahovať triedy zatiaľ. Toto je príležitosť napísať tento popis.

  • 0x73: TC_OBJECT, označujúci nový objekt.
  • 0x72: TC_CLASSDESC.
  • 00 07: Dĺžka názvu triedy.
  • 63 6F 6E 74 61 69 6E: obsahovať, názov triedy.
  • FC BB E6 0E FB CB 60 C7: SerialVersionUID, sériový identifikátor verzie tejto triedy.
  • 0x02: Rôzne vlajky. Tento príznak označuje, že táto trieda podporuje serializáciu.
  • 00 01: Počet polí v tejto triede.

Ďalej musí algoritmus napísať popis pre obsahovaťjediné pole, int containVersion = 11;.

  • 0x49: Kód typu poľa. 49 predstavuje „I“, čo znamená Int.
  • 00 0E: Dĺžka názvu poľa.
  • 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: obsahovaťVerziu, názov poľa.
  • 0x78: TC_ENDBLOCKDATA.

Ďalej serializačný algoritmus skontroluje, či obsahovať má nejaké rodičovské triedy. Ak by sa to stalo, algoritmus by začal písať túto triedu; ale v tomto prípade neexistuje nadtrieda pre obsahovať, takže algoritmus píše TC_NULL.

  • 0x70: TC_NULL.

Nakoniec algoritmus zapíše skutočné údaje spojené s obsahovať.

  • 00 00 00 0B: 11, hodnota obsahovaťVerziu.

Záver

V tomto tipe ste videli, ako serializovať objekt, a podrobne ste sa dozvedeli, ako serializačný algoritmus funguje. Dúfam, že vám tento článok poskytne viac podrobností o tom, čo sa stane, keď objekt skutočne serializujete.

O autorovi

Sathiskumar Palaniappan má viac ako štyri roky skúseností v IT priemysle a už viac ako tri roky pracuje s technológiami súvisiacimi s Java. V súčasnosti pracuje ako inžinier systémového softvéru v Java Technology Center v IBM Labs. Má tiež skúsenosti v telekomunikačnom priemysle.

Zdroje

  • Prečítajte si špecifikáciu serializácie objektov Java. (Špecifikácia je PDF.)
  • „Splošte svoje objekty: Objavte tajomstvá rozhrania Java Serialization API“ (Todd M. Greanier, JavaWorld, júl 2000) ponúka pohľad na základné body procesu serializácie.
  • Kapitola 10 z Java RMI (William Grosso, O'Reilly, október 2001) je tiež užitočným odkazom.

Tento príbeh „Odhalený algoritmus serializácie Java“ pôvodne publikoval server JavaWorld.

$config[zx-auto] not found$config[zx-overlay] not found