जावा क्रमांकन: readObject () बनाम readResolve ()


127

प्रभावी जावा और अन्य स्रोत पुस्तक क्रमिक जावा कक्षाओं के साथ काम करते समय रीडऑबजेक्ट () पद्धति का उपयोग कैसे और कब करें, इस पर बहुत अच्छी व्याख्या प्रदान करते हैं। दूसरी ओर readResolve () विधि, एक रहस्य बनी हुई है। मूल रूप से सभी दस्तावेज़ जो मैंने पाया, दोनों में से केवल एक का उल्लेख करते हैं या दोनों का केवल व्यक्तिगत रूप से उल्लेख करते हैं।

अनुत्तरित रहने वाले प्रश्न हैं:

  • दोनों विधियों में क्या अंतर है?
  • कब किस विधि को लागू किया जाना चाहिए?
  • कैसे पढ़ा जाना चाहिए () का उपयोग किया जाना चाहिए, विशेष रूप से क्या लौटने के संदर्भ में?

मुझे उम्मीद है कि आप इस मामले पर कुछ प्रकाश डाल सकते हैं।


ओरेकल की JDK से उदाहरण:String.CaseInsensitiveComparator.readResolve()
kevinarpe

जवाबों:


138

readResolveधारा से पढ़ी गई वस्तु को बदलने के लिए प्रयोग किया जाता है। एकमात्र प्रयोग जो मैंने इसके लिए देखा है, वह एकल गायन को लागू कर रहा है; जब कोई ऑब्जेक्ट पढ़ा जाता है, तो उसे सिंगलटन उदाहरण से बदल दें। यह सुनिश्चित करता है कि कोई भी एकलिंग को क्रमबद्ध और डिसेर्बलाइज करके एक और उदाहरण नहीं बना सकता है।


3
दुर्भावनापूर्ण कोड (या यहां तक ​​कि डेटा) के चारों ओर पाने के लिए कई तरीके हैं।
टॉम हॉल्टिन -

6
जोश बलोच उन परिस्थितियों के बारे में बात करता है जिसके तहत यह प्रभावी जावा 2 एड में टूट जाता है। आइटम 77. उन्होंने इस बात का उल्लेख इस बात में किया है जो उन्होंने Google IO दंपत्ति को कुछ साल पहले दिया था (कुछ समय बात के अंत की ओर): youtube.com/watch?v=pi_I7oD_uGI
calvinkrishy

17
मुझे यह उत्तर थोड़ा अपर्याप्त लगता है, क्योंकि इसमें transientखेतों का उल्लेख नहीं है । यह पढ़ने के बाद वस्तु को हल करने के readResolveलिए उपयोग किया जाता है। एक उदाहरण का उपयोग शायद एक वस्तु है जो कुछ कैश रखती है जिसे मौजूदा डेटा से फिर से बनाया जा सकता है और इसे क्रमबद्ध करने की आवश्यकता नहीं है; कैश्ड डेटा को घोषित किया जा सकता है और डिसेरिएलाइजेशन के बाद इसका पुनर्निर्माण कर सकता है । ऐसी चीजें जो इस विधि के लिए हैं। transientreadResolve()
जेसन सी

2
@JasonC आपकी टिप्पणी है कि "इस तरह की [क्षणिक संभाल] चीजें हैं जो इस विधि के लिए है " भ्रामक है। इसके लिए जावा डॉक देखें Serializable: यह कहता है "कक्षाएं जिन्हें प्रतिस्थापन से नामित करने की आवश्यकता होती है जब धारा से पढ़ा जाता है तो इसे लागू करना चाहिए [ readResolve] विशेष विधि ..."।
ओफ़र

2
readResolve विधि का उपयोग एक कोने के मामले में भी किया जा सकता है जिसमें मान लें कि आपने बहुत सी वस्तुओं को क्रमबद्ध किया है और उन्हें डेटाबेस में संग्रहीत किया है। यदि बाद के समय में, आप उस डेटा को नए प्रारूप में माइग्रेट करना चाहते हैं, तो आप इसे आसानी से readResolve पद्धति में प्राप्त कर सकते हैं।
नीलेश राजानी

29

आइटम 90, प्रभावी जावा, तीसरा एड कवर readResolveऔर writeReplaceधारावाहिक परदे के पीछे - उनका मुख्य उपयोग। उदाहरण बाहर readObjectऔर writeObjectविधियों को नहीं लिखते हैं क्योंकि वे फ़ील्ड पढ़ने और लिखने के लिए डिफ़ॉल्ट क्रमांकन का उपयोग कर रहे हैं।

readResolveकहा जाता है के बाद readObjectवापस आ गया है (इसके अलावा एक अलग वस्तु पर writeReplaceपहले writeObjectऔर शायद कहा जाता है )। विधि रिटर्न की जगह ऑब्जेक्टthis है वह ऑब्जेक्ट उपयोगकर्ता के पास ObjectInputStream.readObjectवापस आ जाता है और स्ट्रीम में ऑब्जेक्ट पर किसी भी अन्य बैक रेफरेंस को वापस करता है। दोनों readResolveऔर writeReplaceएक ही या अलग-अलग प्रकार की वस्तुओं वापस आ सकते हैं। उसी प्रकार लौटना कुछ मामलों में उपयोगी होता है जहाँ फ़ील्ड होना चाहिए finalऔर या तो पिछड़े संगतता की आवश्यकता होती है या मूल्यों की प्रतिलिपि बनाई जानी चाहिए और / या मान्य होनी चाहिए।

का उपयोग readResolveएकल संपत्ति को लागू नहीं करता है।


9

readResolve का उपयोग डेटा को बदलने के लिए किया जा सकता है जिसे readObject विधि के माध्यम से क्रमबद्ध किया जाता है। उदाहरण के लिए xstream API कुछ विशेषताओं को आरंभ करने के लिए इस सुविधा का उपयोग करता है जो XML में नहीं थे।

http://x-stream.github.io/faq.html#Serialization


1
XML और Xstream Java Serialization के बारे में एक प्रश्न के लिए प्रासंगिक नहीं है, और इस प्रश्न का उत्तर सही ढंग से वर्षों पहले दिया गया था। -1
लोर्ने का मार्किव

5
स्वीकृत उत्तर बताता है कि readResolve किसी ऑब्जेक्ट को बदलने के लिए उपयोग किया जाता है। यह उत्तर उपयोगी अतिरिक्त जानकारी प्रदान करता है कि इसका उपयोग deserialization के दौरान किसी वस्तु को संशोधित करने के लिए किया जा सकता है। XStream को एक उदाहरण के रूप में दिया गया था, न कि केवल संभावित पुस्तकालय जिसमें यह होता है।
1

5

readResolve तब है जब आपको किसी मौजूदा ऑब्जेक्ट को वापस करने की आवश्यकता हो सकती है, उदाहरण के लिए, क्योंकि आप डुप्लिकेट इनपुटों की जांच कर रहे हैं, जिन्हें मर्ज किया जाना चाहिए, या (जैसे अंत में-लगातार वितरित सिस्टम में) क्योंकि यह एक अपडेट है जो आपको पता होने से पहले आ सकता है। किसी भी पुराने संस्करण।


readResolve () मेरे लिए स्पष्ट था, लेकिन फिर भी मेरे मन में कुछ अस्पष्ट सवाल हैं, लेकिन आपका जवाब सिर्फ मेरे दिमाग को पढ़ें, धन्यवाद
रजनी गंगवार

5

readObject () ObjectInputStream class में एक मौजूदा विधि है। अतः वशीकरण पढ़े जाने के समय ऑब्जेक्ट को पढ़ना। आंतरिक विधि को आंतरिक रूप से जांचें कि क्या क्लास ऑब्जेक्ट जो कि पढ़ी जा रही है। विधि का उपयोग कर रही है या नहीं, यदि वह विधि मौजूद है तो यह readResolve विधि को लागू करेगा और उसी को लौटाएगा। उदाहरण।

तो readResolve विधि लिखने का इरादा शुद्ध सिंगलटन डिज़ाइन पैटर्न को प्राप्त करने के लिए एक अच्छा अभ्यास है जहां कोई भी क्रमिक / deserializing द्वारा कोई अन्य उदाहरण प्राप्त नहीं कर सकता है।



2

जब किसी वस्तु को रूपांतरित करने के लिए क्रमांकन का उपयोग किया जाता है ताकि इसे फ़ाइल में सहेजा जा सके, हम एक विधि को ट्रिगर कर सकते हैं, readResolve ()। विधि निजी है और इसे उसी वर्ग में रखा जाता है जिसका ऑब्जेक्ट डिसेरलाइजेशन करते समय पुनर्प्राप्त किया जा रहा है। यह सुनिश्चित करता है कि डिसेरिएलाइजेशन के बाद, जो वस्तु वापस की जाती है, वह वैसी ही होती है जैसा कि धारावाहिक में दिया गया था। अर्थात्,instanceSer.hashCode() == instanceDeSer.hashCode()

readResolve () विधि एक स्थिर विधि नहीं है। बाद in.readObject()में deserialisation कहा जाता है, यह सुनिश्चित करता है कि लौटी हुई वस्तु वही है जो नीचे के रूप में क्रमबद्ध थीout.writeObject(instanceSer)

..
    ObjectOutput out = new ObjectOutputStream(new FileOutputStream("file1.ser"));
    out.writeObject(instanceSer);
    out.close();

इस तरह, यह सिंगलटन डिजाइन पैटर्न कार्यान्वयन में भी मदद करता है , क्योंकि हर बार एक ही उदाहरण वापस किया जाता है।

public static ABCSingleton getInstance(){
    return ABCSingleton.instance; //instance is static 
}

1

मुझे पता है कि यह प्रश्न वास्तव में पुराना है और इसका एक स्वीकृत उत्तर है, लेकिन जैसा कि यह Google खोज में बहुत अधिक है, मैंने सोचा कि मैं वजन करूंगा क्योंकि कोई भी उत्तर तीन मामलों को शामिल नहीं करता है जिन्हें मैं महत्वपूर्ण मानता हूं - मेरे दिमाग में इन के लिए प्राथमिक उपयोग तरीकों। बेशक, सभी मानते हैं कि वास्तव में कस्टम क्रमांकन प्रारूप की आवश्यकता है।

उदाहरण के लिए, संग्रह कक्षाएं लें। किसी लिंक की गई सूची या BST के डिफ़ॉल्ट क्रमांकन के परिणामस्वरूप बहुत कम प्रदर्शन प्राप्त होता है, जिससे तत्वों को क्रम में क्रमबद्ध करने की तुलना में बहुत कम प्रदर्शन होता है। यह और भी सच है अगर कोई संग्रह एक प्रक्षेपण या एक दृश्य है - एक बड़ी संरचना का संदर्भ रखता है जितना कि वह अपने सार्वजनिक एपीआई द्वारा उजागर करता है।

  1. यदि सीरियल किए गए ऑब्जेक्ट में अपरिवर्तनीय फ़ील्ड हैं जिन्हें कस्टम क्रमांकन की आवश्यकता है, तो मूल समाधान writeObject/readObjectअपर्याप्त है, क्योंकि लिखित धारा के भाग को पढ़ने से पहले deserialized ऑब्जेक्ट बनाया जाता है writeObject। लिंक की गई सूची का यह न्यूनतम क्रियान्वयन करें:

    public class List<E> extends Serializable {
        public final E head;
        public final List<E> tail;
    
        public List(E head, List<E> tail) {
            if (head==null)
                throw new IllegalArgumentException("null as a list element");
            this.head = head;
            this.tail = tail;
        }
    
        //methods follow...
    }

इस संरचना को headप्रत्येक लिंक के क्षेत्र को पुनरावर्ती रूप से लिखते हुए क्रमबद्ध किया जा सकता है , उसके बाद एक nullमान। इस तरह के प्रारूप का वर्णन करना हालांकि असंभव हो जाता है: readObjectसदस्य फ़ील्ड्स (अब निश्चित किया गया null) के मूल्यों को बदल नहीं सकता है । यहाँ आओ writeReplace/ readResolveजोड़ी:

private Object writeReplace() {
    return new Serializable() {
        private transient List<E> contents = List.this;

        private void writeObject(ObjectOutputStream oos) {
            List<E> list = contents;
            while (list!=null) {
                oos.writeObject(list.head);
                list = list.tail;
            }
            oos.writeObject(null);
        }

        private void readObject(ObjectInputStream ois) {
            List<E> tail = null;
            E head = ois.readObject();
            if (head!=null) {
                readObject(ois); //read the tail and assign it to this.contents
                this.contents = new List<>(head, this.contents)
            }                     
        }


        private Object readResolve() {
            return this.contents;
        }
    }
}

मुझे खेद है अगर उपरोक्त उदाहरण संकलन (या काम) नहीं करता है, लेकिन उम्मीद है कि यह मेरी बात को स्पष्ट करने के लिए पर्याप्त है। यदि आपको लगता है कि यह एक बहुत दूर का उदाहरण है तो कृपया याद रखें कि जेवीएम पर कई कार्यात्मक भाषाएं चलती हैं और यह दृष्टिकोण उनके मामले में आवश्यक हो जाता है।

  1. हम वास्तव में एक अलग वर्ग की एक वस्तु को अलग करना चाह सकते हैं, जितना हमने लिखा है ObjectOutputStream। यह एक java.util.Listसूची कार्यान्वयन जैसे विचारों के साथ होगा जो लंबे समय से एक स्लाइस को उजागर करता है ArrayList। जाहिर है, पूरी बैकिंग सूची को क्रमबद्ध करना एक बुरा विचार है और हमें केवल देखे गए स्लाइस से तत्वों को लिखना चाहिए। हालाँकि इस पर रोक क्यों है और निर्जनता के बाद अप्रत्यक्ष स्तर बेकार है? हम केवल धारा से तत्वों को एक में पढ़ सकते हैं ArrayListऔर इसे सीधे हमारे दृश्य वर्ग में लपेटने के बजाय वापस कर सकते हैं।

  2. वैकल्पिक रूप से, एक समान प्रतिनिधि वर्ग को क्रमबद्धता के लिए समर्पित होना एक डिजाइन विकल्प हो सकता है। एक अच्छा उदाहरण हमारे क्रमांकन कोड का पुन: उपयोग करना होगा। उदाहरण के लिए, यदि हमारे पास एक बिल्डर वर्ग (स्ट्रिंग के लिए StringBuilder के समान) है, तो हम एक क्रमांकन प्रतिनिधि लिख सकते हैं जो किसी भी संग्रह को धारा में एक खाली बिल्डर लिखकर क्रमबद्ध करता है, उसके बाद संग्रह का आकार और तत्वों द्वारा colection के पुनरावृत्ति द्वारा लौटाया जाता है। देशीकरण में बिल्डर को पढ़ना, सभी बाद में पढ़ने वाले तत्वों को शामिल करना और build()प्रतिनिधियों से अंतिम परिणाम वापस करना शामिल होगा readResolve। उस मामले में हमें केवल संग्रह पदानुक्रम के मूल वर्ग में क्रमांकन लागू करने की आवश्यकता होगी, और वर्तमान या भविष्य के कार्यान्वयन से कोई अतिरिक्त कोड की आवश्यकता नहीं होगी, बशर्ते वे अमूर्त लागू करेंiterator() औरbuilder()विधि (उसी प्रकार के संग्रह को फिर से बनाने के लिए उत्तरार्द्ध - जो अपने आप में एक बहुत ही उपयोगी विशेषता होगी)। एक अन्य उदाहरण एक वर्ग पदानुक्रम होगा जिस कोड को हम पूरी तरह से नियंत्रित नहीं करते हैं - एक तीसरे पक्ष के पुस्तकालय से हमारा आधार वर्ग (तों) किसी भी संख्या में निजी क्षेत्रों में हो सकता है, जिनके बारे में हम कुछ भी नहीं जानते हैं और जो एक संस्करण से दूसरे संस्करण में बदल सकते हैं, ब्रेकिंग हमारे अनुक्रमित वस्तुओं। उस स्थिति में डेटा लिखना और ऑब्जेक्ट को पुन: डिजायरलाइज़ेशन पर फिर से बनाना सुरक्षित होगा।


0

ReadResolve विधि

सीरियलाइज़ेबल और एक्सटर्नेबल क्लासेस के लिए, readResolve मेथड क्लास को कॉलर में वापस आने से पहले स्ट्रीम से पढ़ी गई ऑब्जेक्ट को बदलने / हल करने की अनुमति देता है। ReadResolve पद्धति को लागू करने से, एक वर्ग सीधे अपने स्वयं के उदाहरणों के प्रकारों और उदाहरणों को नियंत्रित कर सकता है। विधि इस प्रकार परिभाषित की गई है:

कोई भी ACCESS-MODIFIER ऑब्जेक्ट readResolve () फेंकता ObjectStreamException;

ReadResolve विधि जब कहा जाता है ObjectInputStream स्ट्रीम से किसी वस्तु को पढ़ गया है और यह फोन करने वाले पर लौटने के लिए तैयारी कर रहा है। ObjectInputStream जाँचता है कि क्या ऑब्जेक्ट का वर्ग readResolve विधि को परिभाषित करता है। यदि विधि को परिभाषित किया गया है, तो ऑब्जेक्ट को नामित करने के लिए स्ट्रीम में ऑब्जेक्ट को अनुमति देने के लिए readResolve विधि को कहा जाता है। लौटाई गई वस्तु एक प्रकार की होनी चाहिए जो सभी उपयोगों के अनुकूल हो। यदि यह संगत नहीं है, तो टाइप बेमेल होने पर ClassCastException को फेंक दिया जाएगा।

उदाहरण के लिए, एक प्रतीक वर्ग बनाया जा सकता है जिसके लिए प्रत्येक प्रतीक बंधन का केवल एक उदाहरण एक आभासी मशीन के भीतर मौजूद था। यह निर्धारित करने के लिए कि प्रतीक पहले से ही परिभाषित था और पहचान बाधा बनाए रखने के लिए preexisting समकक्ष प्रतीक वस्तु को स्थानापन्न करने के लिए readResolve विधि लागू की जाएगी। इस तरह सिम्बॉलिक वस्तुओं की विशिष्टता को संपूर्णता में बनाए रखा जा सकता है।


0

जैसा कि पहले ही उत्तर दिया जा चुका है, readResolveएक निजी विधि है जिसका उपयोग ObjectInputStream में किया जाता है, जो किसी वस्तु को डिसेर्बलाइज करता है। वास्तविक आवृत्ति वापस आने से ठीक पहले इसे कहा जाता है। सिंगलटन के मामले में, यहां हम पहले से मौजूद सिंग्लटन इंस्टेंस संदर्भ को डिसेररलाइज्ड इंस्टेंस संदर्भ के बजाय वापस लौटने के लिए मजबूर कर सकते हैं। Similary हमारे पास writeReplaceObjectOutputStream के लिए है।

इसके लिए उदाहरण readResolve:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class SingletonWithSerializable implements Serializable {
private static final long serialVersionUID = 1L;

public static final SingletonWithSerializable INSTANCE = new SingletonWithSerializable();

private SingletonWithSerializable() {
    if (INSTANCE != null)
        throw new RuntimeException("Singleton instance already exists!");
}

private Object readResolve() {
    return INSTANCE;
}

public void leaveTheBuilding() {
    System.out.println("SingletonWithPublicFinalField.leaveTheBuilding() called...");
}

public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    SingletonWithSerializable instance = SingletonWithSerializable.INSTANCE;

    System.out.println("Before serialization: " + instance);

    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("file1.ser"))) {
        out.writeObject(instance);
    }

    try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("file1.ser"))) {
        SingletonWithSerializable readObject = (SingletonWithSerializable) in.readObject();
        System.out.println("After deserialization: " + readObject);
    }

}

}

आउटपुट:

Before serialization: com.ej.item3.SingletonWithSerializable@7852e922
After deserialization: com.ej.item3.SingletonWithSerializable@7852e922
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.