آشنایی با مبحث شیرین Serialization در جاوا


فرض کنید که شما در برنامه خود یک آبجکت از یک کلاس دارید که یک سری داده هم در اون آبجکت ذخیره کردید و حالا می خواهید این داده ها رو دریک فایل ذخیره کنید چه می کنید؟

زبان جاوا یک مکانیزم جذاب به نام object serialization در اختیار ما قرار می‌دهد، در این مکانیزم می‌تونیم آبجکت رو به شکل دنباله‌ای از بایت‌ها در جریان خروجی بنویسیم و بعدا برش گردونیم. دنباله بایت ها شامل داده‌های آبجکت، نوع آبجکت چیه و نوع داده‌های ذخیره شده در آبجکت چی هست، می‌باشد.

پس از اینکه آبجکت سریال‌سازی شده در یک فایل نوشته شد، می توانیم از فایل اونو بخونیم و deserialize اش کنیم یعنی با استفاده از اطلاعاتی که در بایت‌ها ذخیره شده مثل نوع داده و داده های درون آبجکت، آن آبجکت را در حافظه بازسازی کنیم.(خیلی ساده و آسون!)
جذاب‌ترین بخش این فرآیند آن است که به ماشین مجازی جاوا (JVM) وابسته نیست به طوری‌که می‌تونه در یک پلتفرم serialized شود و سپس در پلتفرم کاملاً متفاوت دیگری deserialized گردد.

حالا می‌خواهم دو تا کلاس مهم رو بهتون معرفی کنم که با استفاده از اون‌ها بتونید خیلی راحت و ساده سریالایز و دی سریالایز رو انجام بدید! کلاس‌های ObjectInputStream و ObjectOutputStream جریان‌های سطح بالایی هستند که در خودشون توابعی برای سریالایز و دی سریالایز کردن آبجکت ها دارند.

کلاس ObjectOutputStream توابع نوشتن زیادی برای نوشتن نوع داده‌های مختلف دارد. اما تابع زیر از همه برجسته‌تر است:

 public final void writeObject(Object x) throws IOException 

تابعی که در بالا می‌بینید یک آبجکت را در ورودی می‌گیرد و اون آبجکت رو سریالایز کرده و به جریان خروجی (output stream) ارسال‌اش می‌کند. خُب پس به طور مشابه کلاس ObjectInputStream هم تابع زیر را برای دی سریالایز کردن آبجکت دارد.

public final Object readObject() throws IOException, ClassNotFoundException

این تابع آبجکت رُ از جریان ورودی (فایلی که قبلا آبجکت توش ذخیره شده) بازیابی و دی سریالایز می‌کند. خروجی این تابع یک آبجکت است که شما باید آن را به کلاس موردنظرتان cast کنید.

حالا این که سریالایز چطور در جاوا کار می کند را از طریق یک مثال با کمک از کلاس Employee نشان می‌دهیم. کلاس Employee زیر را داریم که اینترفیس Serializable را پیاده‌سازی کرده است:

public class Employee implements java.io.Serializable {
   public String name;
   public String address;
   public transient int SSN;
   public int number;
   
   public void mailCheck() {
      System.out.println(&quotMailing a check to &quot + name + &quot &quot + address);
   }
}

توجه کنید برای آن که یک کلاس با موفقیت سریال سازی شود باید دو تا شرط مهم را رعایت کند:

  • شرط اول: کلاس باید اینترفیس java.io.Serializable را پیاده‌سازی کرده باشد.
  • شرط دوم: همه ی فیلدهای کلاس باید قابل سریال‌سازی باشند. اگر فیلدی قابل سریال‌سازی نباشد باید توسط transient نشانه گذاری گردد.

اگر براتون جالب بود که یک کلاس استاندارد در جاوا قابل سریال سازی هست یا خیر، داکیومنت آن را مطالعه کنید. تست آن هم بسیار ساده است، اگر کلاس java.io.Serializable را پیاده‌سازی کند قابل سریال سازی است و اگرنه نیست.

معرفی دی سریالایز (Deserialize)کردن یک آبجکت:

همون طور که گفتیم کلاس ObjectOutputStream برای سریالایز کردن آبجکت استفاده می‌شه. مثالی می‌زنیم از برنامه SerializeDemo که یک آبجکت از Employee می‌سازد و آن را درون یک فایل سریال‌سازی می‌کند. وقتی اجرای برنامه تمام می‌شود یک فایل employee.ser ایجاد شده است.

توجه: وقتی که یک آبجکت را به یک فایل سریالایز می‌کنید، استاندارد جاوا این است که به فایل پسوند .ser بدهد.

import java.io.*;
public class SerializeDemo {

   public static void main(String [] args) {
      Employee e = new Employee();
      e.name = &quotReyan Ali&quot
      e.address = &quotPhokka Kuan, Ambehta Peer&quot
      e.SSN = 11122333;
      e.number = 101;
      
      try {
         FileOutputStream fileOut =
         new FileOutputStream(&quot/tmp/employee.ser&quot);
         ObjectOutputStream out = new ObjectOutputStream(fileOut);
         out.writeObject(e);
         out.close();
         fileOut.close();
         System.out.printf(&quotSerialized data is saved in /tmp/employee.ser&quot);
      } catch (IOException i) {
         i.printStackTrace();
      }
   }
}

خُب حالا یک FileOutputStream رو برای نوشتن بایت های خروجی باز می کنیم و یک مسیر ذخیره فایل هم در سازنده بهش می‌دهیم.

سپس یک شی از کلاس دوست داشتنی ObjectOutputStream می‌گیریم و جریان داده خروجی رو بهش می‌دهیم. حالا وقتی چیه؟ بله متد نوشتن آبجکت در فایل رو فراخوانی کنیم. (متد writeObject )

حالا اگر یادتون باشه، کار این متد، گرفتن آبجکت (نمونه از کلاس Employee) و سریالایز کردن اون و ارسالش به جریان خروجی هست. بعدم که تابع کلوز شی های ساخته شده رو فراخوانی می کنیم که بندگان خدا سرگردون نباشن. (منابع آزاد بشن)

از سریال خارج کردن (Deserializing) آبجکت:

برنامه زیر آبجکت Employee که در برنامه قبلی سریال سازی شده بود را deserialize می‌کند.

import java.io.*;
public class DeserializeDemo {

   public static void main(String [] args) {
      Employee e = null;
      try {
         FileInputStream fileIn = new FileInputStream(&quot/tmp/employee.ser&quot);
         ObjectInputStream in = new ObjectInputStream(fileIn);
         e = (Employee) in.readObject();
         in.close();
         fileIn.close();
      } catch (IOException i) {
         i.printStackTrace();
         return;
      } catch (ClassNotFoundException c) {
         System.out.println(&quotEmployee class not found&quot);
         c.printStackTrace();
         return;
      }
      
      System.out.println(&quotDeserialized Employee...&quot);
      System.out.println(&quotName: &quot + e.name);
      System.out.println(&quotAddress: &quot + e.address);
      System.out.println(&quotSSN: &quot + e.SSN);
      System.out.println(&quotNumber: &quot + e.number);
   }
}

خروجی برنامه بالا به صورت زیر است:

Deserialized Employee...
Name: Reyan Ali
Address:Phokka Kuan, Ambehta Peer
SSN: 0
Number:101

نکات زیر قابل توجه است:

  • بلاک try/catch تلاش می‌کند ClassNotFoundException که در تابع (readObject) اعلان شده است را catch کند. برای آنکه ماشین مجازی جاوا (JVM) بتواند یک آبجکت را deserialize کند باید بایت کد مربوط به کلاس را پیدا کند، و اگر نتواند بایت کد را پیدا کند این اکسپشن را ایجاد می‌کند.
  • توجه کنید که خروجی تابع readObject به رفرنسی به Employee ، فرم دهی (cast) خواهد شد.
  • مقدار ابتدایی فیلد SSN برابر با 11122333 بود، ولی اگر یادتان باشد ما آن را با transient نشانه گذاری کردیم بهمین دلیل در زمان سریال سازی به جریان خروجی ارسال نشد و مقدار آن در آبجکت deserialize شده برابر صفر شد.
    این نوشته ترجمه‌ی آزادی از اینجا بود و اگر می‌خواهید در این مطلب عمیق شوید. همین فصل از کتاب Core Java جلد دوم را در اینجا بخوانید.