JavaRush /وبلاگ جاوا /Random-FA /سریال سازی همانطور که هست. قسمت 1
articles
مرحله

سریال سازی همانطور که هست. قسمت 1

در گروه منتشر شد
در نگاه اول، سریال سازی فرآیندی پیش پا افتاده به نظر می رسد. واقعاً چه چیزی می تواند ساده تر باشد؟ کلاس را برای پیاده سازی رابط اعلام کرد java.io.Serializable- و تمام. شما می توانید کلاس را بدون مشکل سریال کنید. سریال سازی همانطور که هست.  قسمت 1 - 1از نظر تئوری، این درست است. در عمل، ظرافت های زیادی وجود دارد. آنها با عملکرد، به deserialization، به ایمنی کلاس مرتبط هستند. و با بسیاری از جنبه های دیگر. چنین نکات ظریفی مورد بحث قرار خواهد گرفت. این مقاله را می توان به بخش های زیر تقسیم کرد:
  • ظرافت های مکانیسم ها
  • چرا نیاز است؟Externalizable
  • کارایی
  • اما از سوی دیگر
  • امنیت داده ها
  • سریال سازی اشیاءSingleton
بریم سراغ قسمت اول -

ظرافت های مکانیسم ها

اول از همه یک سوال سریع. چند راه برای سریال سازی یک شی وجود دارد؟ تمرین نشان می دهد که بیش از 90٪ از توسعه دهندگان به این سوال تقریباً به همان روش پاسخ می دهند (تا جمله بندی) - فقط یک راه وجود دارد. در همین حال، دو نفر از آنها وجود دارد. همه مورد دوم را به خاطر نمی آورند، چه رسد به اینکه چیزی قابل درک در مورد ویژگی های آن بیان کنند. پس این روش ها چیست؟ اولین مورد را همه به یاد دارند. این پیاده سازی قبلا ذکر شده است java.io.Serializableو نیازی به تلاش ندارد. روش دوم نیز پیاده سازی یک رابط است، اما روشی متفاوت: java.io.Externalizable. بر خلاف java.io.Serializable, شامل دو روش است که باید پیاده سازی شوند - writeExternal(ObjectOutput)و readExternal(ObjectInput). این روش‌ها حاوی منطق سریال‌سازی/آسیالی‌زدایی هستند. اظهار نظر.در ادامه، گاهی اوقات به سریال سازی با پیاده سازی به عنوان استاندارد و پیاده سازی به صورت توسعه یافته Serializableاشاره می کنم Externalizable. یکی دیگراظهار نظر. readObjectمن عمداً اکنون به گزینه های استاندارد کنترل سریال سازی مانند تعریف و ، دست نمی زنم writeObject، زیرا به نظر من این روش ها تا حدودی نادرست هستند. این روش‌ها در رابط تعریف نشده‌اند Serializableو در واقع ابزارهایی برای دور زدن محدودیت‌ها و انعطاف‌پذیر کردن سریال‌سازی استاندارد هستند. Externalizableروش هایی که انعطاف پذیری را فراهم می کنند از همان ابتدا در آنها تعبیه شده است . بیایید یک سوال دیگر بپرسیم. چگونه سریال سازی استاندارد با استفاده از java.io.Serializable? و از طریق Reflection API کار می کند. آن ها کلاس به صورت مجموعه ای از فیلدها تجزیه می شود که هر کدام در جریان خروجی نوشته می شود. من فکر می کنم واضح است که این عملیات از نظر عملکرد بهینه نیست. دقیقا بعدا متوجه میشیم چقدر تفاوت عمده دیگری بین دو روش سریال سازی ذکر شده وجود دارد. یعنی در مکانیزم deserialization. هنگام استفاده، Serializabledeserialization به این صورت اتفاق می افتد: حافظه برای یک شی تخصیص داده می شود، پس از آن فیلدهای آن با مقادیر جریان پر می شود. سازنده شیء فراخوانی نمی شود. در اینجا باید این وضعیت را جداگانه بررسی کنیم. خوب، کلاس ما قابل سریال سازی است. و پدر و مادرش؟ کاملا اختیاری! علاوه بر این، اگر کلاسی را از آن به ارث ببرید Object- والد قطعا قابل سریال سازی نیست. و حتی اگر Objectما چیزی در مورد فیلدها نمی دانیم، ممکن است در کلاس های مادر خود ما وجود داشته باشند. چه اتفاقی برایشان میافتد؟ آنها وارد جریان سریال سازی نخواهند شد. پس از سریال‌زدایی چه ارزش‌هایی خواهند داشت؟ بیایید به این مثال نگاه کنیم:
package ru.skipy.tests.io;

import java.io.*;

/**
 * ParentDeserializationTest
 *
 * @author Eugene Matyushkin aka Skipy
 * @since 05.08.2010
 */
public class ParentDeserializationTest {

    public static void main(String[] args){
        try {
            System.out.println("Creating...");
            Child c = new Child(1);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            c.field = 10;
            System.out.println("Serializing...");
            oos.writeObject(c);
            oos.flush();
            baos.flush();
            oos.close();
            baos.close();
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            System.out.println("Deserializing...");
            Child c1 = (Child)ois.readObject();
            System.out.println("c1.i="+c1.getI());
            System.out.println("c1.field="+c1.getField());
        } catch (IOException ex){
            ex.printStackTrace();
        } catch (ClassNotFoundException ex){
            ex.printStackTrace();
        }
    }

    public static class Parent {
        protected int field;
        protected Parent(){
            field = 5;
            System.out.println("Parent::Constructor");
        }
        public int getField() {
            return field;
        }
    }

    public static class Child extends Parent implements Serializable{
        protected int i;
        public Child(int i){
            this.i = i;
            System.out.println("Child::Constructor");
        }
        public int getI() {
            return i;
        }
    }
}
شفاف است - ما یک کلاس والد غیرقابل سریال و یک کلاس فرزند قابل سریال داریم. و این چیزی است که اتفاق می افتد:
Creating...
Parent::Constructor
Child::Constructor
Serializing...
Deserializing...
Parent::Constructor
c1.i=1
c1.field=5
یعنی در حین deserialization، سازنده بدون پارامترهای کلاس والد NON-serializable نامیده می شود . و اگر چنین سازنده ای وجود نداشته باشد، در حین deserialization خطایی رخ می دهد. همانطور که در بالا گفته شد، سازنده شی فرزند، چیزی که ما آن را deserialize می کنیم، فراخوانی نمی شود. این نحوه رفتار مکانیزم های استاندارد هنگام استفاده است Serializable. هنگام استفاده از آن، Externalizableوضعیت متفاوت است. ابتدا سازنده بدون پارامتر فراخوانی می شود و سپس متد readExternal بر روی شی ایجاد شده فراخوانی می شود که در واقع تمام داده های آن را می خواند. بنابراین، هر کلاسی که رابط Externalizable را پیاده سازی می کند، باید یک سازنده عمومی بدون پارامتر داشته باشد! علاوه بر این، از آنجایی که تمام فرزندان چنین کلاسی نیز برای پیاده سازی اینترفیس در نظر گرفته می شوند Externalizable، باید سازنده بدون پارامتر نیز داشته باشند! بیایید جلوتر برویم. یک اصلاح کننده فیلد مانند وجود دارد transient. یعنی این فیلد نباید سریالی شود. با این حال، همانطور که خودتان متوجه شدید، این دستورالعمل فقط بر مکانیسم سریال سازی استاندارد تأثیر می گذارد. هنگام استفاده، Externalizableهیچ کس زحمت سریال کردن این فیلد و همچنین کم کردن آن را نمی دهد. اگر یک فیلد گذرا اعلام شود، آنگاه هنگامی که شیء از فهرست خارج می شود، مقدار پیش فرض را می گیرد. یک نکته نسبتا ظریف دیگر. با سریال‌سازی استاندارد، فیلدهایی که دارای اصلاح‌کننده هستند staticسریال‌سازی نمی‌شوند. بر این اساس، پس از سریال‌زدایی، این فیلد تغییری در ارزش خود ایجاد نمی‌کند. البته در حین اجرا Externalizableکسی به خود زحمت سریال سازی و سریال زدایی این زمینه را نمی دهد اما به شدت توصیه می کنم این کار را انجام ندهید زیرا این می تواند منجر به خطاهای ظریف شود. فیلدهای دارای اصلاح کننده finalمانند فیلدهای معمولی سریال می شوند. به استثنای یک استثنا - هنگام استفاده از Externalizable نمی توان آنها را deserialized کرد. زیرا final-поляباید در سازنده مقدار دهی اولیه شوند و پس از آن تغییر مقدار این فیلد در readExternal غیرممکن خواهد بود. بر این اساس، اگر شما نیاز به سریال سازی شی ای دارید که دارای final-فیلد است، فقط باید از سریال سازی استاندارد استفاده کنید. نکته دیگری که خیلی ها نمی دانند. سریال سازی استاندارد ترتیبی را که فیلدها در یک کلاس اعلام می شوند را در نظر می گیرد. در هر صورت در نسخه های قبلی اینطور بود؛ در JVM نسخه 1.6 پیاده سازی اوراکل دیگر ترتیب مهم نیست، نوع و نام فیلد مهم است. ترکیب روش‌ها به احتمال زیاد بر مکانیسم استاندارد تأثیر می‌گذارد، علی‌رغم این واقعیت که ممکن است فیلدها به طور کلی ثابت بمانند. برای جلوگیری از این، مکانیسم زیر وجود دارد. به هر کلاسی که رابط را پیاده سازی می کند Serializable، یک فیلد دیگر در مرحله کامپایل اضافه می شود -private static final long serialVersionUID. این فیلد حاوی شناسه نسخه منحصر به فرد کلاس سریال شده است. بر اساس محتویات کلاس - فیلدها، ترتیب اعلان آنها، روش ها، ترتیب اعلان آنها محاسبه می شود. بر این اساس با هر تغییری در کلاس، این فیلد مقدار خود را تغییر می دهد. این فیلد زمانی که کلاس سریال می شود در جریان نوشته می شود. به هر حال، این شاید تنها مورد شناخته شده برای من زمانی که staticیک -فیلد سریال است. در طول سریال‌زدایی، مقدار این فیلد با کلاس موجود در ماشین مجازی مقایسه می‌شود. اگر مقادیر مطابقت نداشته باشند، استثنایی مانند زیر ایجاد می شود:
java.io.InvalidClassException: test.ser2.ChildExt;
    local class incompatible: stream classdesc serialVersionUID = 8218484765288926197,
                                   local class serialVersionUID = 1465687698753363969
با این حال، راهی برای فریب این چک، اگر نه دور زدن، وجود دارد. اگر مجموعه فیلدهای کلاس و ترتیب آنها قبلاً تعریف شده باشد، می تواند مفید باشد، اما متدهای کلاس ممکن است تغییر کنند. در این مورد، سریال‌سازی در خطر نیست، اما مکانیسم استاندارد اجازه نمی‌دهد داده‌ها با استفاده از بایت کد کلاس اصلاح‌شده سریال‌سازی شوند. اما همانطور که گفتم او را می توان فریب داد. یعنی به صورت دستی فیلد را در کلاس تعریف کنید private static final long serialVersionUID. در اصل، ارزش این زمینه می تواند مطلقاً هر چیزی باشد. برخی از افراد ترجیح می دهند آن را برابر با تاریخ تغییر کد تنظیم کنند. برخی حتی از 1 لیتر استفاده می کنند. برای به دست آوردن مقدار استاندارد (مقدار داخلی محاسبه شده)، می توانید از ابزار سریال موجود در SDK استفاده کنید. هنگامی که به این ترتیب تعریف شد، مقدار فیلد ثابت خواهد شد، از این رو deserialization همیشه مجاز خواهد بود. علاوه بر این، در نسخه 5.0، تقریباً موارد زیر در مستندات ظاهر شد: به شدت توصیه می‌شود که همه کلاس‌های قابل سریال‌سازی این فیلد را به صراحت اعلام کنند، زیرا محاسبه پیش‌فرض به جزئیات ساختار کلاس بسیار حساس است، که ممکن است بسته به اجرای کامپایلر متفاوت باشد. و در نتیجه باعث InvalidClassExceptionعواقب غیرمنتظره می شود. بهتر است این فیلد را به عنوان اعلان کنید private، زیرا فقط به کلاسی که در آن اعلان شده است اشاره دارد. اگرچه اصلاح کننده در مشخصات مشخص نشده است. حال اجازه دهید این جنبه را در نظر بگیریم. فرض کنید این ساختار کلاس را داریم:
public class A{
    public int iPublic;
    protected int iProtected;
    int iPackage;
    private int iPrivate;
}

public class B extends A implements Serializable{}
به عبارت دیگر، ما یک کلاس داریم که از یک والد غیرقابل سریال به ارث رسیده است. آیا امکان سریال سازی این کلاس وجود دارد و برای این کار چه چیزی لازم است؟ چه اتفاقی برای متغیرهای کلاس والد خواهد افتاد؟ جواب این است. بله، Bشما می توانید یک نمونه از یک کلاس را سریال کنید. چه چیزی برای این مورد نیاز است؟ اما کلاس باید Aسازنده بدون پارامتر publicیا protected. سپس در حین deserialization، تمام متغیرهای کلاس Aبا استفاده از این سازنده مقداردهی اولیه می شوند. متغیرهای کلاس Bبا مقادیر حاصل از جریان داده سریالی اولیه می شوند. از نظر تئوری، می توان در یک کلاس Bمتدهایی را که در ابتدا در مورد آنها صحبت کردم تعریف کرد - readObjectو , - که در ابتدای آن می توان متغییرهای کلاس را از طریق writeObject(سریالی زدایی) و سپس (سلسله زدایی) متغیرهای موجود انجام داد. از کلاس (در مورد ما اینها هستند ، و ، اگر در همان بسته باشد ). با این حال، به نظر من، بهتر است از سریال سازی توسعه یافته برای این کار استفاده کنید. نکته بعدی که می خواهم به آن اشاره کنم سریال سازی چندین شی است. فرض کنید ساختار کلاس زیر را داریم: Bin.defaultReadObject/out.defaultWriteObjectAiPubliciProtectediPackageBA
public class A implements Serializable{
    private C c;
    private B b;
    public void setC(C c) {this.c = c;}
    public void setB(B b) {this.b = b;}
    public C getC() {return c;}
    public B getB() {return b;}
}
public class B implements Serializable{
    private C c;
    public void setC(C c) {this.c = c;}
    public C getC() {return c;}
}
public class C implements Serializable{
    private A a;
    private B b;
    public void setA(A a) {this.a = a;}
    public void setB(B b) {this.b = b;}
    public B getB() {return b;}
    public A getA() {return a;}
}
سریال سازی همانطور که هست.  قسمت 1 - 2اگر نمونه ای از کلاس را سریال کنید چه اتفاقی می افتد A؟ یک نمونه از کلاس را در امتداد می کشد B، که به نوبه خود، نمونه ای را در امتداد می کشد Cکه ارجاع به نمونه دارد A، همان نمونه ای که همه چیز با آن شروع شد. دایره باطل و بازگشت بی نهایت؟ خوشبختانه خیر. بیایید به کد تست زیر نگاه کنیم:
// initiaizing
A a = new A();
B b = new B();
C c = new C();
// setting references
a.setB(b);
a.setC(c);
b.setC(c);
c.setA(a);
c.setB(b);
// serializing
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(a);
oos.writeObject(b);
oos.writeObject(c);
oos.flush();
oos.close();
// deserializing
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
A a1 = (A)ois.readObject();
B b1 = (B)ois.readObject();
C c1 = (C)ois.readObject();
// testing
System.out.println("a==a1: "+(a==a1));
System.out.println("b==b1: "+(b==b1));
System.out.println("c==c1: "+(c==c1));
System.out.println("a1.getB()==b1: "+(a1.getB()==b1));
System.out.println("a1.getC()==c1: "+(a1.getC()==c1));
System.out.println("b1.getC()==c1: "+(b1.getC()==c1));
System.out.println("c1.getA()==a1: "+(c1.getA()==a1));
System.out.println("c1.getB()==b1: "+(c1.getB()==b1));
ما چه کار می کنیم؟ ما یک نمونه از کلاس ها را ایجاد می کنیم و Aبه آنها پیوندهایی به یکدیگر می دهیم و سپس هر یک از آنها را سریال می کنیم. سپس آن‌ها را بی‌اصلاح می‌کنیم و یک سری بررسی‌ها را انجام می‌دهیم. در نتیجه چه اتفاقی خواهد افتاد: BC
a==a1: false
b==b1: false
c==c1: false
a1.getB()==b1: true
a1.getC()==c1: true
b1.getC()==c1: true
c1.getA()==a1: true
c1.getB()==b1: true
بنابراین چه چیزی می توانید از این آزمون یاد بگیرید؟ اولین. ارجاعات شیء پس از سریال زدایی با مراجع قبل از آن متفاوت است. به عبارت دیگر، در طول سریال‌سازی/آسیالی‌سازی شیء کپی شد. گاهی اوقات از این روش برای شبیه سازی اشیا استفاده می شود. نتیجه دوم مهمتر است. هنگامی که چندین شیء که دارای ارجاع متقابل هستند، سریال‌سازی/آسیالی‌زدایی می‌شود، آن مراجع پس از سریال‌زدایی معتبر باقی می‌مانند. به عبارت دیگر، اگر قبل از سریال سازی به یک شی اشاره می کردند، پس از سریال زدایی نیز به یک شی اشاره می کنند. یک آزمایش کوچک دیگر برای تأیید این موضوع:
B b = new B();
C c = new C();
b.setC(c);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(b);
oos.writeObject(c);
oos.writeObject(c);
oos.writeObject(c);
oos.flush();
oos.close();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
B b1 = (B)ois.readObject();
C c1 = (C)ois.readObject();
C c2 = (C)ois.readObject();
C c3 = (C)ois.readObject();
System.out.println("b1.getC()==c1: "+(b1.getC()==c1));
System.out.println("c1==c2: "+(c1==c2));
System.out.println("c1==c3: "+(c1==c3));
یک شی کلاس Bبه یک شی کلاس ارجاع دارد C. هنگامی که سریال می شود، bهمراه با یک نمونه از کلاس سریال می شود С، پس از آن همان نمونه c سه بار سریال می شود. بعد از سریال‌زدایی چه اتفاقی می‌افتد؟
b1.getC()==c1: true
c1==c2: true
c1==c3: true
همانطور که می بینید، هر چهار شیء deserialized در واقع یک شی را نشان می دهند - ارجاعات به آن برابر است. دقیقا مثل قبل از سریال. نکته جالب دیگر - چه اتفاقی می افتد اگر به طور همزمان اجرا کنیم Externalizableو Serializable? مانند آن سوال - فیل در مقابل نهنگ - چه کسی چه کسی را شکست خواهد داد؟ غلبه خواهد کرد Externalizable. مکانیسم سریال‌سازی ابتدا وجود آن را بررسی می‌کند و فقط بعد از آن وجود آن را بررسی می‌کند، Serializableبنابراین اگر کلاس B که پیاده‌سازی می‌کند Serializable، از کلاس A که پیاده‌سازی می‌کند ارث می‌برد Externalizable، فیلدهای کلاس B سریال‌سازی نمی‌شوند. نکته آخر ارث است. هنگام ارث بردن از کلاسی که پیاده‌سازی می‌کند Serializable، نیازی به انجام اقدامات اضافی نیست. سریال سازی به کلاس کودک نیز گسترش خواهد یافت. هنگام ارث بردن از کلاسی که پیاده‌سازی می‌کند Externalizable، باید متدهای readExternal و writeExternal کلاس والد را لغو کنید. در غیر این صورت فیلدهای کلاس فرزند سریالی نمی شوند. در این مورد، باید به یاد داشته باشید که متدهای والد را فراخوانی کنید، در غیر این صورت فیلدهای والد سریالی نمی شوند. * * * احتمالاً با جزئیات تمام شده ایم. با این حال، یک موضوع وجود دارد که ما به آن اشاره نکرده ایم، که ماهیت جهانی دارد. برای مثال -

چرا به Externalizable نیاز دارید؟

اصلا چرا به سریال سازی پیشرفته نیاز داریم؟ پاسخ ساده است. اولا، انعطاف پذیری بسیار بیشتری می دهد. ثانیاً، اغلب می تواند دستاوردهای قابل توجهی از نظر حجم داده های سریالی ایجاد کند. ثالثاً جنبه ای به عنوان عملکرد وجود دارد که در زیر در مورد آن صحبت خواهیم کرد . به نظر می رسد همه چیز با انعطاف پذیری روشن است. در واقع، ما می‌توانیم فرآیندهای سریال‌سازی و سریال‌زدایی را همانطور که می‌خواهیم کنترل کنیم، که ما را از هرگونه تغییر در کلاس مستقل می‌کند (همانطور که در بالا گفتم، تغییرات در کلاس می‌تواند تا حد زیادی بر deserialization تأثیر بگذارد). بنابراین، من می خواهم چند کلمه در مورد افزایش حجم بگویم. فرض کنید کلاس زیر را داریم:
public class DateAndTime{

  private short year;
  private byte month;
  private byte day;
  private byte hours;
  private byte minutes;
  private byte seconds;

}
بقیه چیز مهمی نیست فیلدها می توانند از نوع int ساخته شوند، اما این فقط اثر مثال را افزایش می دهد. اگرچه در واقعیت ممکن است فیلدها intبه دلایل عملکردی تایپ شوند. در هر صورت موضوع روشن است. کلاس نشان دهنده تاریخ و زمان است. در درجه اول از نظر سریال سازی برای ما جالب است. شاید ساده ترین کار ذخیره یک مهر زمانی ساده باشد. از نوع طولانی است، یعنی. وقتی سریال می شود 8 بایت طول می کشد. علاوه بر این، این رویکرد به روش‌هایی برای تبدیل مؤلفه‌ها به یک مقدار و برگشت نیاز دارد. - کاهش بهره وری مزیت این روش یک تاریخ کاملاً دیوانه کننده است که می تواند در 64 بیت قرار گیرد. این یک حاشیه بزرگ ایمنی است که اغلب در واقعیت مورد نیاز نیست. کلاس داده شده در بالا 2 + 5 * 1 = 7 بایت خواهد بود. به علاوه سربار برای کلاس و 6 فیلد. آیا راهی برای فشرده سازی این داده ها وجود دارد؟ مطمئنا. ثانیه ها و دقیقه ها در محدوده 0-59 هستند، یعنی. برای نمایش آنها، 6 بیت به جای 8 کافی است. ساعت - 0-23 (5 بیت)، روز - 0-30 (5 بیت)، ماه - 0-11 (4 بیت). کل، همه چیز بدون در نظر گرفتن سال - 26 بیت. هنوز 6 بیت به اندازه int باقی مانده است. از نظر تئوری، در برخی موارد این ممکن است برای یک سال کافی باشد. در غیر این صورت، با اضافه کردن یک بایت دیگر، اندازه فیلد داده به 14 بیت افزایش می یابد که محدوده 0-16383 را به دست می دهد. این در برنامه های واقعی بیش از اندازه کافی است. در مجموع حجم داده های مورد نیاز برای ذخیره اطلاعات لازم را به 5 بایت کاهش داده ایم. اگر نه تا 4. نقطه ضعف مانند مورد قبلی است - اگر تاریخ بسته بندی شده را ذخیره کنید، روش های تبدیل مورد نیاز است. اما من می خواهم این کار را به این صورت انجام دهم: آن را در فیلدهای جداگانه ذخیره کنید و آن را به صورت سریالی در قالب بسته بندی کنید. اینجاست که استفاده از آن منطقی است Externalizable:
// data is packed into 5 bytes:
//  3         2         1
// 10987654321098765432109876543210
// hhhhhmmmmmmssssssdddddMMMMyyyyyy yyyyyyyy
public void writeExternal(ObjectOutput out){
    int packed = 0;
    packed += ((int)hours) << 27;
    packed += ((int)minutes) << 21;
    packed += ((int)seconds) << 15;
    packed += ((int)day) << 10;
    packed += ((int)month) << 6;
    packed += (((int)year) >> 8) & 0x3F;
    out.writeInt(packed);
    out.writeByte((byte)year);
}

public void readExternal(ObjectInput in){
    int packed = in.readInt();
    year = in.readByte() & 0xFF;
    year += (packed & 0x3F) << 8;
    month = (packed >> 6) & 0x0F;
    day = (packed >> 10) & 0x1F;
    seconds = (packed >> 15) & 0x3F;
    minutes = (packed >> 21) & 0x3F;
    hours = (packed >> 27);
}
در واقع، این همه است. پس از سریال سازی، سربار به ازای هر کلاس، دو فیلد (به جای 6) و 5 بایت داده دریافت می کنیم. که در حال حاضر به طور قابل توجهی بهتر است. بسته بندی بیشتر را می توان به کتابخانه های تخصصی واگذار کرد. مثال ارائه شده بسیار ساده است. هدف اصلی آن این است که نشان دهد چگونه می توان از سریال سازی پیشرفته استفاده کرد. اگرچه به نظر من افزایش احتمالی در حجم داده های سریالی از مزیت اصلی دور است. مزیت اصلی، علاوه بر انعطاف پذیری... (به آرامی به قسمت بعدی بروید...) لینک به منبع: سریال سازی همانطور که هست
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION