אלגוריתם הסידור של Java גילה

סידור הוא תהליך שמירת מצב האובייקט ברצף בתים; deserialization הוא תהליך של בנייה מחדש של בתים אלה לאובייקט חי. ממשק ה- API של סידור Java מספק מנגנון סטנדרטי למפתחים לטיפול בסידור אובייקטים. בטיפ זה תראו כיצד לסדר אובייקט, ולמה לפעמים יש צורך בסידור. תלמד על אלגוריתם הסידור המשמש ב- Java, ותראה דוגמה הממחישה את הפורמט הסידורי של אובייקט. כשתסיים, תהיה לך ידע מקיף על אופן פעולתו של אלגוריתם הסידור ואילו ישויות מסודרות כחלק מהאובייקט ברמה נמוכה.

מדוע יש צורך בסידור?

בעולם של היום, יישום ארגוני טיפוסי יכלול מספר רכיבים והוא יופץ על פני מערכות ורשתות שונות. בג'אווה הכל מיוצג כאובייקטים; אם שני רכיבי Java רוצים לתקשר זה עם זה, צריך להיות מנגנון להחלפת נתונים. אחת הדרכים להשיג זאת היא להגדיר פרוטוקול משלך ולהעביר אובייקט. המשמעות היא שעל הקצה המקבל להכיר את הפרוטוקול המשמש את השולח ליצירת האובייקט מחדש, מה שיקשה מאוד על דיבור עם רכיבי צד שלישי. לפיכך, צריך להיות פרוטוקול כללי ויעיל כדי להעביר את האובייקט בין רכיבים. סידור מוגדר למטרה זו, ורכיבי Java משתמשים בפרוטוקול זה להעברת אובייקטים.

איור 1 מציג תצוגה ברמה גבוהה של תקשורת לקוח / שרת, כאשר אובייקט מועבר מהלקוח לשרת באמצעות סידור.

איור 1. תצוגה ברמה גבוהה של סידור בפעולה (לחץ להגדלה)

כיצד לסדר אובייקט

על מנת לסדר אובייקט בסידרה, עליך לוודא שהמחלקה של האובייקט מיישמת את java.io.Serializableהממשק, כפי שמוצג ברשימה 1.

רישום 1. יישום ניתן לבצע סדרה

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

ברשימה 1, הדבר היחיד שהיית צריך לעשות אחרת מיצירת מחלקה רגילה הוא יישום java.io.Serializableהממשק. Serializableהממשק הוא ממשק סמן; היא מצהירה על שום שיטות כלל. זה אומר למנגנון הסידור שאפשר לסדר את הכיתה.

כעת, לאחר שהפכת את הכיתה לזכיינית לסידור, השלב הבא הוא לבצע סדרת האובייקט בפועל. זה נעשה על ידי קריאה writeObject()לשיטת java.io.ObjectOutputStreamהכיתה, כפי שמוצג ברשימה 2.

רישום 2. קורא ל- 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(); } 

TestSerialברשימה 2 נשמר מצב האובייקט בקובץ שנקרא temp.out. oos.writeObject(ts);למעשה מתחיל את אלגוריתם הסידור, שבתורו כותב את האובייקט אליו temp.out.

כדי ליצור מחדש את האובייקט מהקובץ המתמיד, תשתמש בקוד ברשימה 3.

רישום 3. יצירת אובייקט סדרתי מחדש

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

ברשימה 3, שחזור האובייקט מתרחש עם oin.readObject()קריאת השיטה. קריאת שיטה זו קוראת בתים הגולמיים שקיימנו בעבר ויוצרת אובייקט חי שהוא העתק מדויק של גרף האובייקט המקורי. מכיוון readObject()שיכול לקרוא כל אובייקט שניתן לסדר, יש צורך בקסטה מהסוג הנכון.

ביצוע קוד זה יודפס version=100על הפלט הסטנדרטי.

הפורמט הסידורי של אובייקט

איך נראית הגרסה הסדרתית של האובייקט? זכור, קוד הדוגמה בסעיף הקודם שמר את הגרסה הסדרתית של TestSerialהאובייקט בקובץ temp.out. רישום 4 מציג את התוכן של temp.outהמוצג בהקסדצימלי. (אתה זקוק לעורך הקסדצימלי כדי לראות את הפלט בפורמט הקסדצימלי.)

רישום 4. צורת הקסדצימלי של 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 

אם תסתכל שוב על TestSerialהאובייקט בפועל , תראה שיש בו רק שני חברי בתים, כפי שמוצג ברשימה 5.

רישום 5. חברי הבתים של TestSerial

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

גודלו של משתנה בתים הוא בת אחד, ומכאן שהגודל הכולל של האובייקט (ללא הכותרת) הוא שני בתים. אבל אם אתה מסתכל על גודל האובייקט הסדרתי ברשימה 4, תראה 51 בתים. הַפתָעָה! מהיכן הגיעו הבתים הנוספים, ומה משמעותם? הם מוצגים על ידי אלגוריתם הסידור, ונדרשים על מנת ליצור מחדש את האובייקט. בחלק הבא תחקור את האלגוריתם הזה בפירוט.

אלגוריתם הסידור של ג'אווה

נכון לעכשיו, אתה אמור להיות בעל ידע די טוב כיצד לסדר אובייקט. אבל איך התהליך עובד מתחת למכסה המנוע? באופן כללי אלגוריתם הסידור עושה את הפעולות הבאות:

  • זה כותב את המטא-נתונים של הכיתה המשויכים למופע.
  • זה כותב רקורטיבי את התיאור של מעמד העל עד שהוא מוצא java.lang.object.
  • לאחר שהוא מסיים לכתוב את המידע על המטא-נתונים, הוא מתחיל בנתונים בפועל המשויכים למופע. אבל הפעם, זה מתחיל ממעמד העל הגבוה ביותר.
  • זה כותב רקורסיבית את הנתונים המשויכים למופע, החל מהמחלקה הפחות-סופית ועד למחלקה הנגזרת ביותר.

כתבתי אובייקט דוגמא אחר לסעיף זה שיכסה את כל המקרים האפשריים. אובייקט הדגימה החדש לסידורי מוצג ברשימה 6.

רישום 6. דוגמת אובייקט סדרתי

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

דוגמה זו היא דוגמה פשוטה. זה SerialTestמסדרת אובייקט מהסוג , שמקורו parentובעל אובייקט מיכל contain,. הפורמט הסידורי של אובייקט זה מוצג ברשימה 7.

רישום 7. צורה סדרתית של אובייקט לדוגמא

 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 

איור 2 מציע מבט ברמה גבוהה על אלגוריתם הסידור עבור תרחיש זה.

איור 2. מתווה של אלגוריתם הסידור

בואו נעבור את הפורמט הסידורי של האובייקט ונראה מה מייצג כל בת. התחל במידע על פרוטוקול הסידור:

  • AC ED: STREAM_MAGIC. מציין שמדובר בפרוטוקול סדרתי.
  • 00 05: STREAM_VERSION. גרסת הסידור.
  • 0x73: TC_OBJECT. מציין שזה חדש 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 ניסיון של יותר מארבע שנים בתעשיית ה- IT, והיא עובדת עם טכנולוגיות הקשורות ל- Java כבר יותר משלוש שנים. נכון לעכשיו הוא עובד כמהנדס תוכנת מערכת במרכז הטכנולוגיה של ג'אווה, מעבדות יבמ. יש לו גם ניסיון בתעשיית הטלקום.

אֶמְצָעִי

  • קרא את מפרט סידור האובייקטים של Java. (Spec הוא PDF).
  • "שטוח את האובייקטים שלך: גלה את סודות ה- API של סידור Java" (Todd M. Greanier, JavaWorld, יולי 2000) מציע מבט על האומים והברגים של תהליך הסידור.
  • פרק 10 ב- Java RMI (ויליאם גרוסו, אוריילי, אוקטובר 2001) הוא גם התייחסות שימושית.

סיפור זה, "אלגוריתם הסידור של ג'אווה שנחשף" פורסם במקור על ידי JavaWorld.