Algoritma serialisasi Java terungkap

Serialisasi adalah proses menyimpan status objek menjadi urutan byte; deserialization adalah proses membangun kembali byte tersebut menjadi objek hidup. Java Serialization API menyediakan mekanisme standar bagi pengembang untuk menangani serialisasi objek. Di tip ini, Anda akan melihat cara membuat serialisasi objek, dan mengapa serialisasi terkadang diperlukan. Anda akan mempelajari tentang algoritme serialisasi yang digunakan di Java, dan melihat contoh yang menggambarkan format serial dari suatu objek. Pada saat Anda selesai, Anda harus memiliki pengetahuan yang kuat tentang cara kerja algoritma serialisasi dan entitas apa yang diserialkan sebagai bagian dari objek pada level rendah.

Mengapa serialisasi diperlukan?

Di dunia sekarang ini, aplikasi perusahaan pada umumnya akan memiliki banyak komponen dan akan didistribusikan ke berbagai sistem dan jaringan. Di Java, semuanya direpresentasikan sebagai objek; Jika dua komponen Java ingin saling berkomunikasi, perlu ada mekanisme pertukaran data. Salah satu cara untuk mencapai ini adalah dengan menentukan protokol Anda sendiri dan mentransfer objek. Ini berarti bahwa penerima harus mengetahui protokol yang digunakan oleh pengirim untuk membuat ulang objek, yang akan menyulitkan untuk berbicara dengan komponen pihak ketiga. Oleh karena itu, diperlukan protokol yang generik dan efisien untuk mentransfer objek antar komponen. Serialisasi didefinisikan untuk tujuan ini, dan komponen Java menggunakan protokol ini untuk mentransfer objek.

Gambar 1 menunjukkan tampilan tingkat tinggi dari komunikasi klien / server, di mana objek ditransfer dari klien ke server melalui serialisasi.

Gambar 1. Tampilan serialisasi tingkat tinggi dalam aksi (klik untuk memperbesar)

Bagaimana membuat serial sebuah objek

Untuk membuat sebuah objek berseri, Anda perlu memastikan bahwa kelas dari objek tersebut mengimplementasikan java.io.Serializableantarmuka, seperti yang ditunjukkan pada Listing 1.

Kode 1. Menerapkan Serializable

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

Dalam Listing 1, satu-satunya hal yang harus Anda lakukan secara berbeda dari membuat kelas normal adalah mengimplementasikan java.io.Serializableantarmuka. The Serializableantarmuka adalah antarmuka penanda; itu menyatakan tidak ada metode sama sekali. Ini memberitahu mekanisme serialisasi bahwa kelas dapat diserialkan.

Sekarang Anda telah membuat kelas memenuhi syarat untuk serialisasi, langkah selanjutnya adalah benar-benar membuat serial objek. Itu dilakukan dengan memanggil writeObject()metode java.io.ObjectOutputStreamkelas, seperti yang ditunjukkan pada Kode 2.

Kode 2. Memanggil 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(); } 

Kode 2 menyimpan status TestSerialobjek dalam file bernama temp.out. oos.writeObject(ts);sebenarnya memulai algoritme serialisasi, yang pada gilirannya menulis objek ke temp.out.

Untuk membuat ulang objek dari file persisten, Anda akan menggunakan kode pada Kode 3.

Kode 3. Menciptakan objek serial

 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); } 

Dalam Listing 3, pemulihan objek terjadi dengan oin.readObject()pemanggilan metode. Panggilan metode ini membaca dalam byte mentah yang sebelumnya kita pertahankan dan membuat objek langsung yang merupakan replika persis dari grafik objek asli. Karena readObject()dapat membaca objek yang dapat diserialkan, diperlukan cast dengan tipe yang benar.

Menjalankan kode ini akan mencetak version=100pada keluaran standar.

Format serial dari suatu objek

Seperti apa tampilan versi serial dari objek tersebut? Ingat, kode sampel di bagian sebelumnya menyimpan versi serial dari TestSerialobjek ke dalam file temp.out. Kode 4 menunjukkan isi temp.out, ditampilkan dalam heksadesimal. (Anda memerlukan editor heksadesimal untuk melihat hasilnya dalam format heksadesimal.)

Kode 4. Bentuk heksadesimal 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 

Jika Anda melihat kembali TestSerialobjek yang sebenarnya , Anda akan melihat bahwa itu hanya memiliki dua anggota byte, seperti yang ditunjukkan pada Kode 5.

Daftar 5. Anggota byte TestSerial

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

Ukuran variabel byte adalah satu byte, dan karenanya ukuran total objek (tanpa header) adalah dua byte. Tetapi jika Anda melihat ukuran dari objek serial di Listing 4, Anda akan melihat 51 byte. Mengherankan! Dari mana byte tambahan itu berasal, dan apa signifikansinya? Mereka diperkenalkan oleh algoritme serialisasi, dan diperlukan untuk membuat ulang objek. Di bagian selanjutnya, Anda akan mempelajari algoritme ini secara mendetail.

Algoritme serialisasi Java

Sekarang, Anda harus memiliki pengetahuan yang cukup baik tentang cara membuat serial suatu objek. Tapi bagaimana prosesnya bekerja di bawah tenda? Secara umum, algoritme serialisasi melakukan hal berikut:

  • Ini menulis metadata kelas yang terkait dengan sebuah instance.
  • Ini secara rekursif menuliskan deskripsi superclass sampai ditemukan java.lang.object.
  • Setelah selesai menulis informasi metadata, itu kemudian dimulai dengan data aktual yang terkait dengan instance. Tapi kali ini, itu dimulai dari superclass paling atas.
  • Ini secara rekursif menulis data yang terkait dengan instance, mulai dari superclass paling sedikit hingga kelas yang paling diturunkan.

Saya telah menulis objek contoh yang berbeda untuk bagian ini yang akan mencakup semua kemungkinan kasus. Objek sampel baru yang akan diserialkan ditunjukkan pada Kode 6.

Kode 6. Contoh objek serial

 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(); } } 

This example is a straightforward one. It serializes an object of type SerialTest, which is derived from parent and has a container object, contain. The serialized format of this object is shown in Listing 7.

Listing 7. Serialized form of sample object

 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 

Figure 2 offers a high-level look at the serialization algorithm for this scenario.

Figure 2. An outline of the serialization algorithm

Let's go through the serialized format of the object in detail and see what each byte represents. Begin with the serialization protocol information:

  • AC ED: STREAM_MAGIC. Specifies that this is a serialization protocol.
  • 00 05: STREAM_VERSION. The serialization version.
  • 0x73: TC_OBJECT. Specifies that this is a new 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 memiliki lebih dari empat tahun pengalaman di industri TI, dan telah bekerja dengan teknologi terkait Java selama lebih dari tiga tahun. Saat ini, dia bekerja sebagai insinyur perangkat lunak sistem di Java Technology Center, IBM Labs. Ia juga memiliki pengalaman di industri telekomunikasi.

Sumber daya

  • Baca spesifikasi serialisasi objek Java. (Spesifikasi adalah PDF.)
  • "Ratakan objek Anda: Temukan rahasia Java Serialization API" (Todd M. Greanier, JavaWorld, Juli 2000) menawarkan gambaran tentang proses serialisasi.
  • Bab 10 dari Java RMI (William Grosso, O'Reilly, Oktober 2001) juga merupakan referensi yang berguna.

Cerita ini, "Algoritma serialisasi Java terungkap" pada awalnya diterbitkan oleh JavaWorld.