Otkriven algoritam Java serializacije

Serijalizacija je postupak spremanja stanja objekta u slijed bajtova; deserializacija je postupak obnove tih bajtova u živi objekt. API za Java serializaciju pruža standardni mehanizam za programere koji obrađuju serializaciju objekata. U ovom ćete savjetu vidjeti kako serializirati objekt i zašto je serializacija ponekad potrebna. Naučit ćete o algoritmu serializacije koji se koristi u Javi i vidjet ćete primjer koji ilustrira serializirani format objekta. Kad završite, trebali biste dobro znati kako funkcionira algoritam serializacije i koji su entiteti serializirani kao dio objekta na niskoj razini.

Zašto je potrebna serializacija?

U današnjem svijetu tipična poslovna aplikacija imat će više komponenata i bit će raspoređena u različitim sustavima i mrežama. U Javi je sve predstavljeno kao objekti; ako dvije Java komponente žele međusobno komunicirati, treba postojati mehanizam za razmjenu podataka. Jedan od načina da se to postigne je definiranje vlastitog protokola i prijenos objekta. To znači da kraj primanja mora znati protokol koji koristi pošiljatelj za ponovno stvaranje objekta, što bi jako otežalo razgovor s komponentama treće strane. Stoga mora postojati generički i učinkovit protokol za prijenos objekta između komponenata. U tu svrhu definirana je serializacija, a Java komponente koriste ovaj protokol za prijenos objekata.

Na slici 1. prikazan je prikaz komunikacije klijent / poslužitelj na visokoj razini, gdje se objekt prenosi s klijenta na poslužitelj serializacijom.

Slika 1. Pogled na serializaciju na visokoj razini (kliknite za uvećanje)

Kako serializirati objekt

Da biste serializirali objekt, morate osigurati da klasa objekta implementira java.io.Serializablesučelje, kao što je prikazano u Popisu 1.

Popis 1. Implementacija serializabilna

 import java.io.Serializable; class TestSerial implements Serializable { public byte version = 100; public byte count = 0; } 

U Popisu 1, jedina stvar koju ste morali učiniti drugačije od stvaranja normalne klase je implementacija java.io.Serializablesučelja. SerializableSučelje je marker sučelje; ne izjavljuje nikakve metode. Kaže mehanizmu serializacije da se klasa može serializirati.

Sad kad ste klasu učinili prihvatljivom za serializaciju, sljedeći je korak zapravo serializacija objekta. To se postiže pozivanjem writeObject()metode java.io.ObjectOutputStreamklase, kao što je prikazano na popisu 2.

Popis 2. Pozivanje writeObject ()

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

Popis 2 pohranjuje stanje TestSerialobjekta u datoteku pod nazivom temp.out. oos.writeObject(ts);zapravo započinje algoritam serializacije, koji zauzvrat zapisuje objekt u temp.out.

Da biste ponovno kreirali objekt iz trajne datoteke, upotrijebili biste kod iz popisa 3.

Popis 3. Ponovno stvaranje serializiranog objekta

 public static void main(String args[]) throws IOException { FileInputStream fis = new FileInputStream("temp.out"); ObjectInputStream oin = new ObjectInputStream(fis); TestSerial ts = (TestSerial) oin.readObject(); System.out.println("version="+ts.version); } 

U popisu 3, obnavljanje objekta događa se s oin.readObject()pozivom metode. Ovaj poziv metode očitava u neobrađenim bajtovima koje smo prethodno ustrajali i stvara živi objekt koji je točna replika izvornog grafa objekta. Budući da readObject()može čitati bilo koji objekt koji se može serirati, potrebno je lijevanje ispravnog tipa.

Izvršenje ovog koda ispisat će se version=100na standardnom izlazu.

Serijalizirani format objekta

Kako izgleda serijalizirana verzija objekta? Zapamtite, uzorak koda u prethodnom odjeljku spremio je serijsku verziju TestSerialobjekta u datoteku temp.out. Popis 4 prikazuje sadržaj temp.out, prikazan u heksadecimalnom obliku. (Potreban vam je heksadecimalni uređivač da biste vidjeli izlaz u heksadecimalnom formatu.)

Popis 4. Heksadecimalni oblik TestSerial-a

 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 

Ako ponovno pogledate stvarni TestSerialobjekt, vidjet ćete da ima samo dva člana bajta, kao što je prikazano na popisu 5.

Popis 5. Članovi bajta TestSeriala

 public byte version = 100; public byte count = 0; 

Veličina bajtne varijable je jedan bajt, pa je stoga ukupna veličina objekta (bez zaglavlja) dva bajta. Ali ako pogledate veličinu serializiranog objekta u Popisu 4, vidjet ćete 51 bajt. Iznenađenje! Odakle dodatni bajtovi i koji je njihov značaj? Uvodi ih algoritam serializacije i potrebni su za ponovno stvaranje objekta. U sljedećem ćete odjeljku detaljno istražiti ovaj algoritam.

Javin algoritam serializacije

Do sada biste trebali imati prilično dobro znanje o tome kako serializirati objekt. Ali kako postupak radi ispod haube? Općenito algoritam serializacije radi sljedeće:

  • Zapisuje metapodatke klase povezane s instancom.
  • Rekurzivno ispisuje opis superklase dok ga ne pronađe java.lang.object.
  • Kad završi s pisanjem podataka o metapodacima, započinje sa stvarnim podacima povezanim s instancom. Ali ovaj put započinje od najviše superklase.
  • Rekurzivno zapisuje podatke povezane s instancom, počevši od najmanje superklase do najviše izvedene klase.

Za ovaj sam odjeljak napisao drugačiji primjer predmeta koji će obuhvatiti sve moguće slučajeve. Novi uzorak objekta koji će se serijalizirati prikazan je na popisu 6.

Popis 6. Uzorak serializiranog objekta

 class parent implements Serializable { int parentVersion = 10; } class contain implements Serializable{ int containVersion = 11; } public class SerialTest extends parent implements Serializable { int version = 66; contain con = new contain(); public int getVersion() { return version; } public static void main(String args[]) throws IOException { FileOutputStream fos = new FileOutputStream("temp.out"); ObjectOutputStream oos = new ObjectOutputStream(fos); SerialTest st = new SerialTest(); oos.writeObject(st); oos.flush(); oos.close(); } } 

Ovaj je primjer jednostavan. To serializes objekt tipa SerialTest, koji je izveden iz parentte ima objekt spremnika, contain. Serijalizirani format ovog objekta prikazan je na popisu 7.

Popis 7. Serijalizirani oblik uzorka objekta

 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 

Slika 2 nudi pogled na visoku razinu algoritma serializacije za ovaj scenarij.

Slika 2. Pregled algoritma serializacije

Detaljno ćemo proći kroz serializirani format objekta i vidjeti što svaki bajt predstavlja. Započnite s informacijama o protokolu za serializaciju:

  • AC ED: STREAM_MAGIC. Navodi da je ovo protokol za serializaciju.
  • 00 05: STREAM_VERSION. Verzija za serializaciju.
  • 0x73: TC_OBJECT. Navodi da je ovo novo Object.

The first step of the serialization algorithm is to write the description of the class associated with an instance. The example serializes an object of type SerialTest, so the algorithm starts by writing the description of the SerialTest class.

  • 0x72: TC_CLASSDESC. Specifies that this is a new class.
  • 00 0A: Length of the class name.
  • 53 65 72 69 61 6c 54 65 73 74: SerialTest, the name of the class.
  • 05 52 81 5A AC 66 02 F6: SerialVersionUID, the serial version identifier of this class.
  • 0x02: Various flags. This particular flag says that the object supports serialization.
  • 00 02: Number of fields in this class.

Next, the algorithm writes the field int version = 66;.

  • 0x49: Field type code. 49 represents "I", which stands for Int.
  • 00 07: Length of the field name.
  • 76 65 72 73 69 6F 6E: version, the name of the field.

And then the algorithm writes the next field, contain con = new contain();. This is an object, so it will write the canonical JVM signature of this field.

  • 0x74: TC_STRING. Represents a new string.
  • 00 09: Length of the string.
  • 4C 63 6F 6E 74 61 69 6E 3B: Lcontain;, the canonical JVM signature.
  • 0x78: TC_ENDBLOCKDATA, the end of the optional block data for an object.

The next step of the algorithm is to write the description of the parent class, which is the immediate superclass of SerialTest.

  • 0x72: TC_CLASSDESC. Specifies that this is a new class.
  • 00 06: Length of the class name.
  • 70 61 72 65 6E 74: SerialTest, the name of the class
  • 0E DB D2 BD 85 EE 63 7A: SerialVersionUID, the serial version identifier of this class.
  • 0x02: Various flags. This flag notes that the object supports serialization.
  • 00 01: Number of fields in this class.

Now the algorithm will write the field description for the parent class. parent has one field, int parentVersion = 100;.

  • 0x49: Field type code. 49 represents "I", which stands for Int.
  • 00 0D: Length of the field name.
  • 70 61 72 65 6E 74 56 65 72 73 69 6F 6E: parentVersion, the name of the field.
  • 0x78: TC_ENDBLOCKDATA, the end of block data for this object.
  • 0x70: TC_NULL, which represents the fact that there are no more superclasses because we have reached the top of the class hierarchy.

So far, the serialization algorithm has written the description of the class associated with the instance and all its superclasses. Next, it will write the actual data associated with the instance. It writes the parent class members first:

  • 00 00 00 0A: 10, the value of parentVersion.

Then it moves on to SerialTest.

  • 00 00 00 42: 66, the value of version.

The next few bytes are interesting. The algorithm needs to write the information about the contain object, shown in Listing 8.

Listing 8. The contain object

 contain con = new contain(); 

Remember, the serialization algorithm hasn't written the class description for the contain class yet. This is the opportunity to write this description.

  • 0x73: TC_OBJECT, designating a new object.
  • 0x72: TC_CLASSDESC.
  • 00 07: Length of the class name.
  • 63 6F 6E 74 61 69 6E: contain, the name of the class.
  • FC BB E6 0E FB CB 60 C7: SerialVersionUID, the serial version identifier of this class.
  • 0x02: Various flags. This flag indicates that this class supports serialization.
  • 00 01: Number of fields in this class.

Next, the algorithm must write the description for contain's only field, int containVersion = 11;.

  • 0x49: Field type code. 49 represents "I", which stands for Int.
  • 00 0E: Length of the field name.
  • 63 6F 6E 74 61 69 6E 56 65 72 73 69 6F 6E: containVersion, the name of the field.
  • 0x78: TC_ENDBLOCKDATA.

Next, the serialization algorithm checks to see if contain has any parent classes. If it did, the algorithm would start writing that class; but in this case there is no superclass for contain, so the algorithm writes TC_NULL.

  • 0x70: TC_NULL.

Finally, the algorithm writes the actual data associated with contain.

  • 00 00 00 0B: 11, the value of containVersion.

Conclusion

In this tip, you have seen how to serialize an object, and learned how the serialization algorithm works in detail. I hope this article gives you more detail on what happens when you actually serialize an object.

About the author

Sathiskumar Palaniappan ima više od četiri godine iskustva u IT industriji i više od tri godine radi s tehnologijama povezanim s Javom. Trenutno radi kao inženjer sistemskog softvera u Java tehnološkom centru, IBM Labs. Također ima iskustva u telekom industriji.

Resursi

  • Pročitajte specifikaciju serializacije Java objekta. (Specifikacija je PDF.)
  • "Poravnajte svoje predmete: otkrijte tajne Java API-ja za serializaciju" (Todd M. Greanier, JavaWorld, srpanj 2000.) nudi uvid u matice i postupke procesa serializacije.
  • Poglavlje 10 Java RMI (William Grosso, O'Reilly, listopad 2001.) također je korisna referenca.

Ovu priču, "Otkriveni algoritam Java serializacije" izvorno je objavio JavaWorld.