JavaRush /جاوا بلاگ /Random-UR /سیریلائزیشن جیسا کہ یہ ہے۔ حصہ 1
articles
سطح

سیریلائزیشن جیسا کہ یہ ہے۔ حصہ 1

گروپ میں شائع ہوا۔
پہلی نظر میں، سیریلائزیشن ایک معمولی عمل کی طرح لگتا ہے. واقعی، کیا آسان ہو سکتا ہے؟ انٹرفیس کو نافذ کرنے کے لیے کلاس کا اعلان کیا java.io.Serializable- اور بس۔ آپ بغیر کسی پریشانی کے کلاس کو سیریلائز کر سکتے ہیں۔ سیریلائزیشن جیسا کہ یہ ہے۔  حصہ 1 - 1نظریاتی طور پر، یہ سچ ہے. عملی طور پر، بہت ساری باریکیاں ہیں۔ ان کا تعلق کارکردگی، ڈی سیریلائزیشن، کلاس سیفٹی سے ہے۔ اور بہت سے پہلوؤں کے ساتھ۔ ایسی باریکیوں پر بات کی جائے گی۔ اس مضمون کو درج ذیل حصوں میں تقسیم کیا جا سکتا ہے۔
  • میکانزم کی باریکیاں
  • اس کی ضرورت کیوں ہے؟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 کے ذریعے کام کرتا ہے۔ وہ. کلاس کو فیلڈز کے سیٹ کے طور پر پارس کیا جاتا ہے، جن میں سے ہر ایک آؤٹ پٹ اسٹریم میں لکھا جاتا ہے۔ میرے خیال میں یہ واضح ہے کہ یہ آپریشن کارکردگی کے لحاظ سے بہترین نہیں ہے۔ ہم بعد میں معلوم کریں گے کہ کتنا ٹھیک ہے۔ ذکر کردہ دو سیریلائزیشن طریقوں کے درمیان ایک اور بڑا فرق ہے۔ یعنی ڈی سیریلائزیشن میکانزم میں۔ جب استعمال کیا جاتا ہے تو، Serializableڈی سیریلائزیشن اس طرح ہوتی ہے: میموری کو کسی شے کے لیے مختص کیا جاتا ہے، جس کے بعد اس کے فیلڈز ندی کی قدروں سے بھر جاتے ہیں۔ آبجیکٹ کے کنسٹرکٹر کو نہیں کہا جاتا ہے۔ یہاں ہمیں اس صورت حال پر الگ سے غور کرنے کی ضرورت ہے۔ ٹھیک ہے، ہماری کلاس سیریلائز ہو سکتی ہے۔ اور اس کے والدین؟ مکمل طور پر اختیاری! مزید یہ کہ، اگر آپ کو ایک کلاس وراثت میں ملتی ہے تو 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
یعنی، ڈی سیریلائزیشن کے دوران، پیرنٹ نان سیریلائز کلاس کے پیرامیٹرز کے بغیر کنسٹرکٹر کہلاتا ہے ۔ اور اگر ایسا کوئی کنسٹرکٹر نہیں ہے تو ڈیسیریلائزیشن کے دوران ایک خرابی واقع ہوگی۔ چائلڈ آبجیکٹ کا کنسٹرکٹر، جسے ہم ڈی سیریلائز کر رہے ہیں، نہیں کہا جاتا، جیسا کہ اوپر کہا گیا ہے۔ اس طرح معیاری میکانزم استعمال کرتے وقت برتاؤ کرتے ہیں Serializable۔ اس کا استعمال کرتے وقت، Externalizableصورت حال مختلف ہے. سب سے پہلے، پیرامیٹرز کے بغیر کنسٹرکٹر کو بلایا جاتا ہے، اور پھر تخلیق شدہ آبجیکٹ پر readExternal طریقہ کو بلایا جاتا ہے، جو دراصل اس کے تمام ڈیٹا کو پڑھتا ہے۔ لہذا، ایکسٹرنلائز ایبل انٹرفیس کو نافذ کرنے والی کسی بھی کلاس کے پاس پیرامیٹر کے بغیر پبلک کنسٹرکٹر ہونا ضروری ہے! مزید برآں، چونکہ ایسی کلاس کی تمام اولادوں کو بھی انٹرفیس کو لاگو کرنے پر غور کیا جائے گا Externalizable، ان کے پاس بھی پیرامیٹر لیس کنسٹرکٹر ہونا ضروری ہے! مزید آگے چلتے ہیں۔ ایک ایسا فیلڈ موڈیفائر ہے جیسا کہ transient. اس کا مطلب یہ ہے کہ اس فیلڈ کو سیریلائز نہیں کیا جانا چاہئے۔ تاہم، جیسا کہ آپ خود سمجھتے ہیں، یہ ہدایت صرف معیاری سیریلائزیشن میکانزم کو متاثر کرتی ہے۔ استعمال ہونے پر، Externalizableکوئی بھی اس فیلڈ کو سیریلائز کرنے کے ساتھ ساتھ اسے منہا کرنے کی زحمت نہیں کرتا ہے۔ اگر کسی فیلڈ کو عارضی قرار دیا جاتا ہے، تو جب آبجیکٹ کو ڈی سیریلائز کیا جاتا ہے، تو یہ ڈیفالٹ ویلیو پر ہوتا ہے۔ ایک اور بلکہ لطیف نکتہ۔ معیاری سیریلائزیشن کے ساتھ، جن فیلڈز میں ترمیم کنندہ ہے staticوہ سیریلائز نہیں ہوتے ہیں۔ اس کے مطابق، ڈی سیریلائزیشن کے بعد یہ فیلڈ اپنی قدر نہیں بدلتی ہے۔ بلاشبہ، عمل درآمد کے دوران، Externalizableکوئی بھی اس فیلڈ کو سیریلائز اور ڈی سیریلائز کرنے کی زحمت نہیں کرتا، لیکن میں انتہائی سفارش کرتا ہوں کہ ایسا نہ کریں، کیونکہ یہ ٹھیک ٹھیک غلطیوں کی قیادت کر سکتے ہیں. موڈیفائر والے فیلڈز کو finalریگولر کی طرح سیریلائز کیا جاتا ہے۔ ایک رعایت کے ساتھ - Externalizable استعمال کرتے وقت ان کو ڈی سیریلائز نہیں کیا جا سکتا۔ کیونکہ 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۔ اصول میں، اس میدان کی قدر بالکل کچھ بھی ہو سکتا ہے. کچھ لوگ اسے کوڈ میں ترمیم کی تاریخ کے برابر سیٹ کرنے کو ترجیح دیتے ہیں۔ کچھ یہاں تک کہ 1L استعمال کرتے ہیں۔ معیاری قدر حاصل کرنے کے لیے (جس کا اندرونی طور پر حساب کیا جاتا ہے)، آپ SDK میں شامل سیریلور یوٹیلیٹی استعمال کر سکتے ہیں۔ ایک بار اس طرح سے وضاحت کرنے کے بعد، فیلڈ کی قدر طے ہو جائے گی، اس لیے ڈی سیریلائزیشن کی ہمیشہ اجازت ہوگی۔ مزید برآں، ورژن 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. پھر، ڈی سیریلائزیشن کے دوران، Aاس کنسٹرکٹر کا استعمال کرتے ہوئے تمام کلاس متغیرات کو شروع کیا جائے گا۔ کلاس متغیرات کو Bسیریلائزڈ ڈیٹا اسٹریم کی اقدار کے ساتھ شروع کیا جائے گا۔ نظریاتی طور پر، کلاس میں Bان طریقوں کی وضاحت کرنا ممکن ہے جن کے بارے میں میں نے شروع میں بات کی تھی - readObjectاور writeObject، - جس کے آغاز میں کلاس متغیرات کی سیریلائزیشن (de-) Bاور in.defaultReadObject/out.defaultWriteObjectپھر دستیاب متغیرات کی سیریلائزیشن (de-) کلاس سے A(ہمارے معاملے میں یہ ہیں iPublic، iProtectedاور iPackage، اگر Bیہ اسی پیکیج میں ہے A)۔ تاہم، میری رائے میں، اس کے لئے توسیع شدہ سیریلائزیشن کا استعمال کرنا بہتر ہے. اگلا نکتہ جس پر میں چھونا چاہتا ہوں وہ ہے ایک سے زیادہ اشیاء کی سیریلائزیشن۔ ہم کہتے ہیں کہ ہمارے پاس درج ذیل طبقاتی ڈھانچہ ہے:
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، Bاور C، انہیں ایک دوسرے سے لنک دیتے ہیں، اور پھر ان میں سے ہر ایک کو سیریلائز کرتے ہیں۔ پھر ہم ان کو دوبارہ ڈی سیریلائز کرتے ہیں اور چیکوں کا ایک سلسلہ چلاتے ہیں۔ اس کے نتیجے میں کیا ہوگا:
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
جیسا کہ آپ دیکھ سکتے ہیں، چاروں ڈیسیریلائزڈ اشیاء دراصل ایک شے کی نمائندگی کرتی ہیں - اس کے حوالے برابر ہیں۔ بالکل اسی طرح جیسے یہ سیریلائزیشن سے پہلے تھا۔ ایک اور دلچسپ نکتہ - کیا ہوگا اگر ہم بیک وقت عمل درآمد کریں Externalizableاور Serializable؟ اس سوال کی طرح - ہاتھی بمقابلہ وہیل - کون کس کو شکست دے گا؟ قابو پا لیں گے Externalizable۔ سیریلائزیشن میکانزم پہلے اس کی موجودگی کی جانچ کرتا ہے، اور صرف اس کے بعد اس کی موجودگی کے لیے۔ Serializableلہذا اگر کلاس B، جو لاگو کرتا ہے Serializable، کلاس A سے وراثت میں ملتا ہے، جو لاگو کرتا ہے Externalizable، کلاس B کے فیلڈز کو سیریلائز نہیں کیا جائے گا۔ آخری نکتہ وراثت ہے۔ جب کسی ایسے طبقے سے وراثت ملتی ہے جو لاگو کرتی ہے Serializable، کوئی اضافی کارروائی کرنے کی ضرورت نہیں ہے۔ سیریلائزیشن بچوں کی کلاس تک بھی پھیلے گی۔ لاگو کرنے والی کلاس سے وراثت میں آتے وقت Externalizable، آپ کو پیرنٹ کلاس کے readExternal اور writeExternal طریقوں کو اوور رائڈ کرنا ہوگا۔ بصورت دیگر، چائلڈ کلاس کے شعبوں کو سیریلائز نہیں کیا جائے گا۔ اس صورت میں، آپ کو پیرنٹ طریقوں کو کال کرنا یاد رکھنا ہوگا، بصورت دیگر پیرنٹ فیلڈز کو سیریلائز نہیں کیا جائے گا۔ *** ہم نے شاید تفصیلات کے ساتھ کام کر لیا ہے۔ تاہم، ایک مسئلہ ہے جسے ہم نے چھوا نہیں، عالمی نوعیت کا ہے۔ یعنی -

آپ کو Externalizable کی ضرورت کیوں ہے؟

ہمیں اعلی درجے کی سیریلائزیشن کی ضرورت کیوں ہے؟ جواب بہت سادہ ہے۔ سب سے پہلے، یہ بہت زیادہ لچک دیتا ہے. دوم، یہ اکثر سیریلائزڈ ڈیٹا کے حجم کے لحاظ سے اہم فوائد فراہم کر سکتا ہے۔ سوم، کارکردگی جیسا ایک پہلو ہے، جس کے بارے میں ہم ذیل میں بات کریں گے ۔ لچک کے ساتھ سب کچھ واضح نظر آتا ہے۔ درحقیقت، ہم اپنی مرضی کے مطابق سیریلائزیشن اور ڈی سیریلائزیشن کے عمل کو کنٹرول کر سکتے ہیں، جو ہمیں کلاس میں ہونے والی کسی بھی تبدیلی سے آزاد بناتا ہے (جیسا کہ میں نے اوپر کہا، کلاس میں تبدیلیاں ڈی سیریلائزیشن کو بہت زیادہ متاثر کر سکتی ہیں)۔ لہذا، میں حجم میں فائدہ کے بارے میں چند الفاظ کہنا چاہتا ہوں. ہم کہتے ہیں کہ ہمارے پاس درج ذیل کلاس ہے:
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 کی حد میں ہیں، یعنی ان کی نمائندگی کرنے کے لیے، 8 کے بجائے 6 بٹس کافی ہیں۔ گھنٹے - 0-23 (5 بٹس)، دن - 0-30 (5 بٹس)، مہینے - 0-11 (4 بٹس)۔ کل، سال کو مدنظر رکھے بغیر سب کچھ - 26 بٹس۔ int کے سائز میں اب بھی 6 بٹس باقی ہیں۔ نظریاتی طور پر، بعض صورتوں میں یہ ایک سال کے لیے کافی ہو سکتا ہے۔ اگر نہیں، تو ایک اور بائٹ شامل کرنے سے ڈیٹا فیلڈ کا سائز 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