2009-05-21 12 views
59

जावा में, यदि कोई वर्ग Serializable लागू करता है लेकिन सार है, तो क्या इसे एक धारावाहिक VersionUID लंबे घोषित किया जाना चाहिए, या उप-वर्गों को केवल इसकी आवश्यकता होती है?क्या एक अमूर्त वर्ग में एक serialVersionUID

इस मामले में यह वास्तव में यह इरादा है कि सभी उप वर्ग धारावाहिकता के साथ सौदा करते हैं क्योंकि इस प्रकार के उद्देश्य आरएमआई कॉल में उपयोग किया जाना है।

+3

मैं एक उत्तर लिखना जारी रखता हूं, फिर महसूस करता हूं कि मुझे काफी जानकारी नहीं है, हालांकि मेरे पास एक झटका है। एक प्रश्न के लिए +1 मैं जवाब नहीं दे सकता। –

उत्तर

40

धारावाहिक वस्तु और कक्षा के वर्तमान संस्करण के बीच संगतता निर्धारित करने के लिए धारावाहिक VersionUID प्रदान किया जाता है। इस प्रकार, यह कक्षा के पहले संस्करण में, या इस मामले में, एक सार आधार वर्ग में वास्तव में आवश्यक नहीं है। आपके पास धारावाहिक/deserialize करने के लिए उस सार वर्ग का एक उदाहरण कभी नहीं होगा, इसलिए इसे एक serialVersionUID की आवश्यकता नहीं है।

(बेशक, यह एक संकलक चेतावनी, आप से छुटकारा पाने के लिए चाहते हैं जो उत्पन्न, सही है?)

यह पता चला जेम्स 'टिप्पणी सही है। एक सार आधार वर्ग का serialVersionUID उपclasses के लिए प्रचारित हो जाता है। उस के प्रकाश में, आप को अपने बेस क्लास में serialVersionUID की आवश्यकता है।

कोड परीक्षण करने के लिए:

import java.io.Serializable; 

public abstract class Base implements Serializable { 

    private int x = 0; 
    private int y = 0; 

    private static final long serialVersionUID = 1L; 

    public String toString() 
    { 
     return "Base X: " + x + ", Base Y: " + y; 
    } 
} 



import java.io.FileInputStream; 
import java.io.FileOutputStream; 
import java.io.ObjectInputStream; 
import java.io.ObjectOutputStream; 

public class Sub extends Base { 

    private int z = 0; 

    private static final long serialVersionUID = 1000L; 

    public String toString() 
    { 
     return super.toString() + ", Sub Z: " + z; 
    } 

    public static void main(String[] args) 
    { 
     Sub s1 = new Sub(); 
     System.out.println(s1.toString()); 

     // Serialize the object and save it to a file 
     try { 
      FileOutputStream fout = new FileOutputStream("object.dat"); 
      ObjectOutputStream oos = new ObjectOutputStream(fout); 
      oos.writeObject(s1); 
      oos.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     Sub s2 = null; 
     // Load the file and deserialize the object 
     try { 
      FileInputStream fin = new FileInputStream("object.dat"); 
      ObjectInputStream ois = new ObjectInputStream(fin); 
      s2 = (Sub) ois.readObject(); 
      ois.close(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 

     System.out.println(s2.toString()); 
    } 
} 

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

java.io.InvalidClassException: Base; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 
+0

अच्छा उत्तर ... @SuppressWarnings ("धारावाहिक") चेतावनी संदेश –

+1

@Ryan दबाएगा: धन्यवाद, लेकिन मैं आमतौर पर त्रुटियों की तरह चेतावनियों का इलाज करता हूं और सीधे उनके साथ सौदा करता हूं। –

+1

... लेकिन मैं समझता हूं कि हर कोई इसके बारे में डरावना नहीं है जैसा कि मैं हूं, इसलिए आपकी टिप्पणी की सराहना की जाती है। –

6

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

यदि आप कुछ फैंसी करने की कोशिश कर रहे हैं तो अन्य विकल्प भी हैं। मुझे यकीन नहीं है कि आपका मतलब क्या है "यह उप वर्गों का इरादा है ..."। क्या आप कस्टम सीरियलाइजेशन विधियों को लिखने जा रहे हैं (उदाहरण के लिए लिखेंऑब्जेक्ट, readObject)? यदि ऐसा है तो सुपर क्लास से निपटने के लिए अन्य विकल्प हैं।

देखें: http://java.sun.com/javase/6/docs/api/java/io/Serializable.html

HTH टॉम

2

वास्तव में, टॉम की लिंक की ओर इशारा करते हुए अगर लापता serialVersionID वास्तव में संकलन

दौरान नहीं क्रमबद्धता क्रम यानी करके की जाती है एक serializable वर्ग नहीं खाता है तो स्पष्ट रूप से serialVersionUID घोषित करें, फिर धारावाहिक रनटाइम उस क्लै के लिए डिफ़ॉल्ट धारावाहिक VERSIONUID मान की गणना करेगा वर्ग के विभिन्न पहलुओं के आधार पर एसएस

इससे चीजें जेआरई के विभिन्न संस्करणों के साथ और भी जटिल हो जाती हैं।

0

वैचारिक रूप से, धारावाहिक डेटा कुछ ऐसा दिखाई:

subClassData(className + version + fieldNames + fieldValues) 
parentClassData(className + version + fieldNames + fieldValues) 
... (up to the first parent, that implements Serializable) 

तो जब आप deserialize, पदानुक्रम में कक्षाओं में से किसी में संस्करण में बेमेल अक्रमांकन विफल कारण बनता है। इंटरफेस के लिए कुछ भी संग्रहीत नहीं है, इसलिए उनके लिए संस्करण निर्दिष्ट करने की आवश्यकता नहीं है।

उत्तर: हाँ, आपको आधार सारणी कक्षा में serialVersionUID प्रदान करने की आवश्यकता है। यहां तक ​​कि यदि इसमें फ़ील्ड नहीं हैं (className + version कोई फ़ील्ड नहीं होने पर भी संग्रहीत हैं)।

  1. वर्ग एक फ़ील्ड नहीं है, तो उस धारावाहिक डेटा में पाया जाता है (किसी निकाले क्षेत्र), यह नजरअंदाज कर दिया है:

    इसके अलावा निम्नलिखित पर ध्यान दें।

  2. यदि कक्षा में कोई फ़ील्ड है, जो धारावाहिक डेटा (एक नया फ़ील्ड) में मौजूद नहीं है, तो यह 0/false/null पर सेट है (डिफ़ॉल्ट मान के रूप में डिफ़ॉल्ट रूप से नहीं)।
  3. यदि कोई फ़ील्ड डेटाटाइप बदलता है, तो deserialized मान नए प्रकार के लिए असाइन किया जाना चाहिए। जैसे यदि आपके पास Object फ़ील्ड String मान के साथ है, तो फ़ील्ड प्रकार को String में बदलना सफल होगा, लेकिन Integer में बदलना नहीं होगा। हालांकि, int से long से फ़ील्ड बदलना काम नहीं करेगा, भले ही आप int मान long वैरिएबल को असाइन कर सकें।
  4. यदि सबक्लास अब मूल वर्ग को विस्तारित नहीं करता है, जो इसे धारावाहिक डेटा में विस्तारित करता है, तो इसे अनदेखा किया जाता है (जैसा कि 1 में होता है)।
  5. यदि कोई सबक्लस अब कक्षा को बढ़ाता है, जो धारावाहिक डेटा में नहीं मिलता है, तो पैरेंट क्लास फ़ील्ड 0/false/null value (जैसे 2 में) के साथ पुनर्स्थापित किया जाता है।

सरल शब्दों में: आप फ़ील्ड को पुन: व्यवस्थित कर सकते हैं, उन्हें जोड़ सकते हैं और हटा सकते हैं, यहां तक ​​कि कक्षा पदानुक्रम भी बदल सकते हैं। आपको फ़ील्ड या कक्षाओं का नाम नहीं बदलना चाहिए (यह असफल नहीं होगा, लेकिन मानों को deserialized नहीं किया जाएगा)। आप आदिम प्रकार के साथ फ़ील्ड्स के प्रकार को नहीं बदल सकते हैं, और आप संदर्भ प्रकार फ़ील्ड बदल सकते हैं बशर्ते नए प्रकार सभी मानों से असाइन किए जा सकें।

नोट: यदि बेस क्लास Serializable लागू नहीं करता है और केवल subclass करता है, तो बेस क्लास के फ़ील्ड transient के रूप में व्यवहार करेंगे।

संबंधित मुद्दे