Algoritma serialisasi Java diturunkan

Serialisasi adalah proses menyimpan keadaan objek ke urutan bait; deserialisasi adalah proses membina semula bait tersebut menjadi objek hidup. Java Serialization API menyediakan mekanisme standard bagi pemaju untuk menangani serialisasi objek. Dalam petua ini, anda akan melihat cara membuat siri objek, dan mengapa siriisasi kadang-kadang diperlukan. Anda akan belajar mengenai algoritma serialisasi yang digunakan di Java, dan melihat contoh yang menggambarkan format objek bersiri. Pada saat anda selesai, anda semestinya mempunyai pengetahuan yang kukuh tentang bagaimana algoritma serialisasi berfungsi dan entiti apa yang bersiri sebagai bahagian objek pada tahap rendah.

Mengapa penggambaran diperlukan?

Di dunia sekarang ini, aplikasi perusahaan khas akan mempunyai banyak komponen dan akan diedarkan di pelbagai sistem dan rangkaian. Di Jawa, semuanya dilambangkan sebagai objek; jika dua komponen Java ingin berkomunikasi antara satu sama lain, perlu ada mekanisme untuk bertukar data. Salah satu cara untuk mencapainya adalah dengan menentukan protokol anda sendiri dan memindahkan objek. Ini bermaksud bahawa penerima mesti tahu protokol yang digunakan oleh pengirim untuk membuat semula objek, yang akan menjadikannya sangat sukar untuk berbicara dengan komponen pihak ketiga. Oleh itu, perlu ada protokol generik dan cekap untuk memindahkan objek antara komponen. Serialisasi ditentukan untuk tujuan ini, dan komponen Java menggunakan protokol ini untuk memindahkan objek.

Gambar 1 menunjukkan pandangan tingkat tinggi komunikasi klien / pelayan, di mana objek dipindahkan dari klien ke pelayan melalui serialisasi.

Gambar 1. Pandangan tahap tinggi dari serialisasi dalam tindakan (klik untuk membesarkan)

Cara membuat siri objek

Untuk membuat siri objek, anda perlu memastikan bahawa kelas objek tersebut menggunakan java.io.Serializableantara muka, seperti yang ditunjukkan dalam Penyenaraian 1.

Penyenaraian 1. Melaksanakan Serializable

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

Dalam Penyenaraian 1, satu-satunya perkara yang harus anda lakukan secara berbeza daripada membuat kelas biasa adalah melaksanakan java.io.Serializableantara muka. Antara Serializablemuka adalah antara muka penanda; ia menyatakan tidak ada kaedah sama sekali. Ia memberitahu mekanisme bersiri bahawa kelas boleh dibuat secara bersiri.

Sekarang setelah anda membuat kelas layak untuk serialisasi, langkah seterusnya adalah untuk benar-benar membuat serialisasi objek. Itu dilakukan dengan memanggil writeObject()kaedah java.io.ObjectOutputStreamkelas, seperti yang ditunjukkan dalam Penyenaraian 2.

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

Penyenaraian 2 menyimpan keadaan TestSerialobjek dalam fail yang dipanggil temp.out. oos.writeObject(ts);sebenarnya memulakan algoritma serialisasi, yang seterusnya menulis objek untuk temp.out.

Untuk membuat semula objek dari fail berterusan, anda akan menggunakan kod dalam Penyenaraian 3.

Penyenaraian 3. Mencipta semula objek bersiri

 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 Penyenaraian 3, pemulihan objek berlaku dengan oin.readObject()kaedah panggilan. Panggilan kaedah ini dibaca dalam bait mentah yang sebelumnya kita buat dan membuat objek langsung yang merupakan replika grafik objek asal. Kerana readObject()dapat membaca apa-apa objek yang boleh diselaraskan, diperlukan pemeran untuk jenis yang betul.

Melaksanakan kod ini akan dicetak version=100pada output standard.

Format objek bersiri

Seperti apakah bentuk objek bersiri itu? Ingat, contoh kod di bahagian sebelumnya menyimpan versi bersiri TestSerialobjek ke dalam fail temp.out. Penyenaraian 4 menunjukkan kandungan temp.out, dipaparkan dalam heksadesimal. (Anda memerlukan editor heksadesimal untuk melihat output dalam format heksadesimal.)

Penyenaraian 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 

Sekiranya anda melihat semula TestSerialobjek sebenar , anda akan melihat bahawa ia hanya mempunyai dua anggota bait, seperti yang ditunjukkan dalam Penyenaraian 5.

Penyenaraian 5. Ahli bait TestSerial

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

Ukuran pemboleh ubah bait adalah satu bait, dan oleh itu ukuran keseluruhan objek (tanpa tajuk) adalah dua bait. Tetapi jika anda melihat ukuran objek bersiri dalam Penyenaraian 4, anda akan melihat 51 bait. Kejutan! Dari mana bait tambahan berasal, dan apakah kepentingannya? Mereka diperkenalkan oleh algoritma serialisasi, dan diperlukan untuk membuat kembali objek. Di bahagian seterusnya, anda akan meneroka algoritma ini secara terperinci.

Algoritma serialisasi Java

Pada masa ini, anda sudah semestinya memiliki pengetahuan yang cukup baik tentang cara membuat siri objek. Tetapi bagaimana prosesnya berfungsi di bawah tudung? Secara umum algoritma serialisasi melakukan perkara berikut:

  • Ia menulis metadata kelas yang berkaitan dengan instance.
  • Ini secara berulang-ulang menuliskan keterangan superclass sehingga ia dijumpai java.lang.object.
  • Setelah selesai menulis maklumat metadata, ia kemudian dimulakan dengan data sebenar yang berkaitan dengan instance. Tetapi kali ini, ia bermula dari superclass paling atas.
  • Ini secara berulang-ulang menulis data yang berkaitan dengan contoh, bermula dari kelas superkelas paling rendah hingga kelas paling banyak diperoleh.

Saya telah menulis objek contoh yang berbeza untuk bahagian ini yang akan merangkumi semua kemungkinan kes. Objek sampel baru yang akan disenaraikan ditunjukkan dalam Penyenaraian 6.

Penyenaraian 6. Contoh objek bersiri

 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 pengalaman lebih dari empat tahun dalam industri IT, dan telah bekerja dengan teknologi yang berkaitan dengan Java selama lebih dari tiga tahun. Pada masa ini, dia bekerja sebagai jurutera perisian sistem di Pusat Teknologi Java, IBM Labs. Dia juga mempunyai pengalaman dalam industri telekomunikasi.

Sumber

  • Baca spesifikasi serialisasi objek Java. (Spesifikasi adalah PDF.)
  • "Ratakan objek anda: Temukan rahsia API Serialisasi Java" (Todd M. Greanier, JavaWorld, Julai 2000) memberikan gambaran tentang inti dan baut proses serialisasi.
  • Bab 10 RMI Java (William Grosso, O'Reilly, Oktober 2001) juga merupakan rujukan yang berguna.

Kisah ini, "Algoritma serialisasi Java diungkapkan" pada awalnya diterbitkan oleh JavaWorld.