नमस्ते! आज के पाठ में, हम जावा में क्रमांकन और अक्रमांकन के बारे में बात करते हैं। हम एक साधारण उदाहरण के साथ शुरुआत करेंगे। मान लीजिए आपने एक कंप्यूटर गेम बनाया है। यदि आप 90 के दशक में पले-बढ़े हैं और उस युग के गेम कंसोल को याद करते हैं, तो आप शायद जानते हैं कि उनमें कुछ कमी थी जिसे हम आज मान लेते हैं - गेम को बचाने और लोड करने की क्षमता :) यदि नहीं, तो कल्पना कीजिए! मुझे डर है कि आज इन क्षमताओं के बिना एक खेल बर्बाद हो जाएगा! वैसे भी गेम को "सेव" और "लोड" करने का क्या मतलब है? ठीक है, हम सामान्य अर्थ समझते हैं: हम खेल को उस स्थान से जारी रखना चाहते हैं जहां से हमने छोड़ा था। ऐसा करने के लिए, हम एक प्रकार का "चेक पॉइंट" बनाते हैं, जिसका उपयोग हम गेम को लोड करने के लिए करते हैं। लेकिन एक आकस्मिक गेमर के बजाय एक प्रोग्रामर के लिए इसका क्या मतलब है? उत्तर सरल है: हम'. मान लीजिए कि आप स्ट्रेटेजियम में स्पेन के रूप में खेल रहे हैं। आपके खेल की एक अवस्था होती है: कौन किस क्षेत्र का मालिक है, किसके पास कितने संसाधन हैं, कौन किसके साथ गठबंधन में है, कौन किसके साथ युद्ध में है, इत्यादि। भविष्य में इसे पुनर्स्थापित करने और खेल को जारी रखने के लिए हमें किसी तरह इस जानकारी, हमारे कार्यक्रम की स्थिति को सहेजना चाहिए। इसके लिए ठीक वही है जो क्रमबद्धता और विमुद्रीकरण के लिए है। क्रमांकन बाइट्स के अनुक्रम में किसी वस्तु की स्थिति को संग्रहीत करने की प्रक्रिया है। अक्रमांकनइन बाइट्स से किसी वस्तु को पुनर्स्थापित करने की प्रक्रिया है। किसी भी जावा ऑब्जेक्ट को बाइट अनुक्रम में परिवर्तित किया जा सकता है। हमें इसकी आवश्यकता क्यों होगी? हमने एक से अधिक बार कहा है कि कार्यक्रम अपने आप में मौजूद नहीं होते हैं। अधिकतर, वे अन्य कार्यक्रमों के साथ बातचीत करते हैं, डेटा का आदान-प्रदान करते हैं, आदि। और एक बाइट अनुक्रम एक सुविधाजनक और कुशल प्रारूप है। उदाहरण के लिए, हम अपनी
SavedGame
वस्तु को बाइट्स के अनुक्रम में बदल सकते हैं, इन बाइट्स को नेटवर्क पर दूसरे कंप्यूटर पर भेज सकते हैं, और फिर दूसरे कंप्यूटर पर इन बाइट्स को वापस जावा ऑब्जेक्ट में बदल सकते हैं! मुश्किल लगता है, है ना? और इस प्रक्रिया को लागू करना दर्द की तरह लगता है: / खुशी से, ऐसा नहीं है! :) जावा में,Serializable
क्रमांकन प्रक्रिया के लिए इंटरफ़ेस जिम्मेदार है। यह इंटरफ़ेस अत्यंत सरल है: इसका उपयोग करने के लिए आपको एक ही विधि लागू करने की आवश्यकता नहीं है! हमारा गेम सेविंग क्लास इस तरह दिखता है:
import java.io.Serializable;
import java.util.Arrays;
public class SavedGame implements Serializable {
private static final long serialVersionUID = 1L;
private String[] territoriesInfo;
private String[] resourcesInfo;
private String[] diplomacyInfo;
public SavedGame(String[] territoriesInfo, String[] resourcesInfo, String[] diplomacyInfo){
this.territoriesInfo = territoriesInfo;
this.resourcesInfo = resourcesInfo;
this.diplomacyInfo = diplomacyInfo;
}
public String[] getTerritoriesInfo() {
return territoriesInfo;
}
public void setTerritoriesInfo(String[] territoriesInfo) {
this.territoriesInfo = territoriesInfo;
}
public String[] getResourcesInfo() {
return resourcesInfo;
}
public void setResourcesInfo(String[] resourcesInfo) {
this.resourcesInfo = resourcesInfo;
}
public String[] getDiplomacyInfo() {
return diplomacyInfo;
}
public void setDiplomacyInfo(String[] diplomacyInfo) {
this.diplomacyInfo = diplomacyInfo;
}
@Override
public String toString() {
return "SavedGame{" +
"territoriesInfo=" + Arrays.toString(territoriesInfo) +
", resourcesInfo=" + Arrays.toString(resourcesInfo) +
", diplomacyInfo=" + Arrays.toString(diplomacyInfo) +
'}';
}
}
प्रदेशों, संसाधनों और कूटनीति के बारे में जानकारी के लिए तीन सरणियाँ जिम्मेदार हैं। Serializable इंटरफ़ेस जावा वर्चुअल मशीन को बताता है: " सब कुछ ठीक है - यदि आवश्यक हो, तो इस वर्ग की वस्तुओं को क्रमबद्ध किया जा सकता है "। एक इंटरफ़ेस के बिना एक इंटरफ़ेस अजीब लगता है:/यह आवश्यक क्यों है? इस प्रश्न का उत्तर ऊपर देखा जा सकता है: यह केवल जावा वर्चुअल मशीन को आवश्यक जानकारी प्रदान करने के लिए कार्य करता है। हमारे पिछले पाठों में से एक में, हमने संक्षेप में मार्कर इंटरफेस का उल्लेख किया था । ये विशेष सूचनात्मक इंटरफेस हैं जो हमारी कक्षाओं को अतिरिक्त जानकारी के साथ चिह्नित करते हैं जो भविष्य में जावा मशीन के लिए उपयोगी होगी। उनके पास कोई तरीका नहीं है जिसे आपको लागू करना है।Serializable
उन इंटरफेस में से एक है। एक अन्य महत्वपूर्ण बिंदु: हमें उस private static final long serialVersionUID
चर की आवश्यकता क्यों है जिसे हमने कक्षा में परिभाषित किया है? इसकी आवश्यकता क्यों है? इस क्षेत्र में क्रमबद्ध वर्ग के संस्करण के लिए एक विशिष्ट पहचानकर्ता है । इंटरफ़ेस लागू करने वाले किसी भी वर्ग में पहचानकर्ता Serializable
होता है version
। इसकी गणना वर्ग की सामग्री के आधार पर की जाती है: इसके क्षेत्र, जिस क्रम में वे घोषित किए जाते हैं, विधियाँ, आदि। यदि हम अपनी कक्षा में फ़ील्ड का प्रकार और/या फ़ील्ड की संख्या बदलते हैं, तो संस्करण पहचानकर्ता तुरंत बदल जाता है . serialVersionUID
कक्षा क्रमबद्ध होने पर भी लिखा जाता है। जब हम deserialize करने की कोशिश करते हैं, यानी बाइट्स के एक सेट से किसी ऑब्जेक्ट को पुनर्स्थापित करते हैं, तो संबंधित की serialVersionUID
तुलना मूल्य के साथ की जाती हैserialVersionUID
हमारे कार्यक्रम में कक्षा के लिए। यदि मान मेल नहीं खाते हैं, तो java.io. अमान्य क्लास अपवाद फेंक दिया जाएगा। इसका एक उदाहरण हम नीचे देखेंगे। इससे बचने के लिए, हम केवल अपनी कक्षा में संस्करण पहचानकर्ता को मैन्युअल रूप से सेट करते हैं। हमारे मामले में, यह केवल 1 के बराबर होगा (लेकिन आप अपनी पसंद के किसी अन्य नंबर को स्थानापन्न कर सकते हैं)। खैर, यह हमारी SavedGame
वस्तु को क्रमबद्ध करने का प्रयास करने का समय है और देखें कि क्या होता है!
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Main {
public static void main(String[] args) throws IOException {
// Create our object
String[] territoryInfo = {"Spain has 6 provinces", "Russia has 10 provinces", "France has 8 provinces"};
String[] resourcesInfo = {"Spain has 100 gold", "Russia has 80 gold", "France has 90 gold"};
String[] diplomacyInfo = {"France is at war with Russia, Spain has taken a neutral position"};
SavedGame savedGame = new SavedGame(territoryInfo, resourcesInfo, diplomacyInfo);
// Create 2 streams to serialize the object and save it to a file
FileOutputStream outputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
// Save the game to a file
objectOutputStream.writeObject(savedGame);
// Close the stream and free resources
objectOutputStream.close();
}
}
जैसा कि आप देख सकते हैं, हमने 2 धाराएँ बनाई हैं: FileOutputStream
और ObjectOutputStream
. पहला फ़ाइल में डेटा लिख सकता है, और दूसरा ऑब्जेक्ट को बाइट्स में परिवर्तित करता है। आप पहले से ही समान "नेस्टेड" निर्माणों को देख चुके हैं, उदाहरण के लिए, new BufferedReader(new InputStreamReader(...))
पिछले पाठों में, इसलिए उन्हें आपको डराना नहीं चाहिए :) दो धाराओं की ऐसी "श्रृंखला" बनाकर, हम दोनों कार्य करते हैं: हम वस्तु को SavedGame
एक सेट में परिवर्तित करते हैं बाइट्स की और writeObject()
विधि का उपयोग करके इसे फ़ाइल में सहेजें । और, वैसे, हमने यह भी नहीं देखा कि हमें क्या मिला! यह फाइल देखने का समय है! *ध्यान दें: आपको पहले से फ़ाइल बनाने की ज़रूरत नहीं है। यदि उस नाम की फ़ाइल मौजूद नहीं है, तो यह अपने आप बन जाएगी* और इसकी सामग्री यहां दी गई है!
¬н sr SavedGame [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТVзй{G xp t pФранция воюет СЃ Россией, Рспания заняла позицию нейтралитетаuq ~ t "РЈ Рспании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Рспании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
उह-ओह :( ऐसा लगता है कि हमारा प्रोग्राम काम नहीं करता था :( वास्तव में, यह काम करता था। आपको याद है कि हमने फ़ाइल में बाइट्स का एक सेट भेजा था, न कि केवल एक वस्तु या पाठ? अच्छा, यह वह है जो बाइट्स का सेट ऐसा दिखता है :) यह हमारा सहेजा गया गेम है! अगर हम अपनी मूल वस्तु को पुनर्स्थापित करना चाहते हैं, यानी खेल को वहीं से शुरू करना और जारी रखना चाहते हैं जहां हमने छोड़ा था, तो हमें रिवर्स प्रक्रिया की आवश्यकता है: डिसेरिएलाइज़ेशन। यहां बताया गया है कि यह हमारे में कैसा दिखेगा मामला:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
SavedGame savedGame = (SavedGame) objectInputStream.readObject();
System.out.println(savedGame);
}
}
और यहाँ परिणाम है!
SavedGame{territoriesInfo=["Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces], resourcesInfo=[Spain has 100 gold, Russia has 80 gold, France has 90 gold], diplomacyInfo=[France is at war with Russia, Spain has taken a neutral position]}
उत्कृष्ट! हम पहले अपने खेल की स्थिति को एक फ़ाइल में सहेजने में कामयाब रहे, और फिर इसे फ़ाइल से पुनर्स्थापित कर दिया। अब हम वही काम करने की कोशिश करते हैं, लेकिन हमारी SavedGame
कक्षा के लिए संस्करण पहचानकर्ता के बिना। हम अपनी दोनों कक्षाओं को दोबारा नहीं लिखेंगे। उनका कोड वही रहेगा, लेकिन हम कक्षा private static final long serialVersionUID
से निकाल देंगे। यहाँ क्रमांकन के बाद हमारी वस्तु है:SavedGame
¬н sr SavedGameі€MіuОm‰ [ diplomacyInfot [Ljava/lang/String;[ resourcesInfoq ~ [ territoriesInfoq ~ xpur [Ljava.lang.String;ТVзй{G xp t pФранция воюет СЃ Россией, Рспания заняла позицию нейтралитетаuq ~ t "РЈ Рспании 100 золотаt РЈ Р РѕСЃСЃРёРё 80 золотаt !РЈ Франции 90 золотаuq ~ t &РЈ Рспании 6 провинцийt %РЈ Р РѕСЃСЃРёРё 10 провинцийt &РЈ Франции 8 провинций
लेकिन देखें कि क्या होता है जब हम इसे डिसेर्बलाइज करने की कोशिश करते हैं:
InvalidClassException: local class incompatible: stream classdesc serialVersionUID = -196410440475012755, local class serialVersionUID = -6675950253085108747
यह वही अपवाद है जिसका हमने ऊपर उल्लेख किया है। वैसे, हम कुछ महत्वपूर्ण चूक गए। यह समझ में आता है कि स्ट्रिंग्स और प्राइमेटिव्स को आसानी से क्रमबद्ध किया जा सकता है: ऐसा करने के लिए जावा में शायद किसी प्रकार का अंतर्निहित तंत्र है। लेकिन क्या होगा अगर हमारी serializable
कक्षा में ऐसे क्षेत्र हैं जो आदिम नहीं हैं, बल्कि अन्य वस्तुओं के संदर्भ हैं? उदाहरण के लिए, आइए अपनी कक्षा के साथ काम करने के लिए अलग TerritoriesInfo
और कक्षाएं बनाएं ResourcesInfo
।DiplomacyInfo
SavedGame
public class TerritoriesInfo {
private String info;
public TerritoriesInfo(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "TerritoriesInfo{" +
"info='" + info + '\'' +
'}';
}
}
public class ResourcesInfo {
private String info;
public ResourcesInfo(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "ResourcesInfo{" +
"info='" + info + '\'' +
'}';
}
}
public class DiplomacyInfo {
private String info;
public DiplomacyInfo(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
@Override
public String toString() {
return "DiplomacyInfo{" +
"info='" + info + '\'' +
'}';
}
}
और अब एक सवाल उठता है: क्या इन सभी वर्गों को होना चाहिए Serializable
अगर हम अपनी बदली हुई SavedGame
कक्षा को क्रमबद्ध करना चाहते हैं?
import java.io.Serializable;
import java.util.Arrays;
public class SavedGame implements Serializable {
private TerritoriesInfo territoriesInfo;
private ResourcesInfo resourcesInfo;
private DiplomacyInfo diplomacyInfo;
public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
this.territoriesInfo = territoriesInfo;
this.resourcesInfo = resourcesInfo;
this.diplomacyInfo = diplomacyInfo;
}
public TerritoriesInfo getTerritoriesInfo() {
return territoriesInfo;
}
public void setTerritoriesInfo(TerritoriesInfo territoriesInfo) {
this.territoriesInfo = territoriesInfo;
}
public ResourcesInfo getResourcesInfo() {
return resourcesInfo;
}
public void setResourcesInfo(ResourcesInfo resourcesInfo) {
this.resourcesInfo = resourcesInfo;
}
public DiplomacyInfo getDiplomacyInfo() {
return diplomacyInfo;
}
public void setDiplomacyInfo(DiplomacyInfo diplomacyInfo) {
this.diplomacyInfo = diplomacyInfo;
}
@Override
public String toString() {
return "SavedGame{" +
"territoriesInfo=" + territoriesInfo +
", resourcesInfo=" + resourcesInfo +
", diplomacyInfo=" + diplomacyInfo +
'}';
}
}
खैर, इसका परीक्षण करते हैं! चलो सब कुछ छोड़ दें जैसा है और किसी SavedGame
वस्तु को क्रमबद्ध करने का प्रयास करें:
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Main {
public static void main(String[] args) throws IOException {
// Create our object
TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
DiplomacyInfo diplomacyInfo = new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");
SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(savedGame);
objectOutputStream.close();
}
}
परिणाम:
Exception in thread "main" java.io.NotSerializableException: DiplomacyInfo
यह काम नहीं किया! मूल रूप से, यह हमारे प्रश्न का उत्तर है। जब किसी वस्तु को क्रमबद्ध किया जाता है, तो उसके आवृत्ति चर द्वारा संदर्भित सभी वस्तुओं को क्रमबद्ध किया जाता है। और यदि वे वस्तुएँ अन्य वस्तुओं को भी संदर्भित करती हैं, तो वे भी क्रमबद्ध हैं। और इसी तरह अनंत तक। इस श्रंखला में सभी वर्ग होने चाहिएSerializable
, अन्यथा उन्हें क्रमबद्ध करना असंभव होगा और एक अपवाद फेंक दिया जाएगा। वैसे, यह सड़क के नीचे समस्याएं पैदा कर सकता है। हमें क्या करना चाहिए, उदाहरण के लिए, जब हम क्रमबद्ध करते हैं तो हमें कक्षा के भाग की आवश्यकता नहीं होती है? या, उदाहरण के लिए, क्या होगा यदि TerritoryInfo
कक्षा किसी तीसरे पक्ष के पुस्तकालय के हिस्से के रूप में हमारे पास आए। और मान लीजिए कि यह नहीं है Serializable
और तदनुसार, हम इसे बदल नहीं सकते हैं। TerritoryInfo
यह पता चला है कि हम अपने क्षेत्र में कोई फ़ील्ड नहीं जोड़ सकते हैंSavedGame
कक्षा, क्योंकि ऐसा करने से पूरी SavedGame
कक्षा गैर-क्रमबद्ध हो जाएगी! यह एक समस्या है:/ जावा में, इस तरह की समस्याओं को transient
कीवर्ड का उपयोग करके हल किया जाता है। यदि आप इस खोजशब्द को अपनी कक्षा के किसी क्षेत्र में जोड़ते हैं, तो वह क्षेत्र क्रमबद्ध नहीं होगा। आइए SavedGame
कक्षा के उदाहरण क्षेत्रों में से एक को क्षणिक बनाने का प्रयास करें। फिर हम एक वस्तु को क्रमबद्ध और पुनर्स्थापित करेंगे।
import java.io.Serializable;
public class SavedGame implements Serializable {
private transient TerritoriesInfo territoriesInfo;
private ResourcesInfo resourcesInfo;
private DiplomacyInfo diplomacyInfo;
public SavedGame(TerritoriesInfo territoriesInfo, ResourcesInfo resourcesInfo, DiplomacyInfo diplomacyInfo) {
this.territoriesInfo = territoriesInfo;
this.resourcesInfo = resourcesInfo;
this.diplomacyInfo = diplomacyInfo;
}
// ...getters, setters, toString()
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class Main {
public static void main(String[] args) throws IOException {
// Create our object
TerritoryInfo territoryInfo = new TerritoryInfo("Spain has 6 provinces, Russia has 10 provinces, France has 8 provinces");
ResourceInfo resourceInfo = new ResourceInfo("Spain has 100 gold, Russia has 80 gold, France has 90 gold");
DiplomacyInfo diplomacyInfo = new DiplomacyInfo("France is at war with Russia, Spain has taken a neutral position");
SavedGame savedGame = new SavedGame(territoriesInfo, resourcesInfo, diplomacyInfo);
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(savedGame);
objectOutputStream.close();
}
}
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
SavedGame savedGame = (SavedGame) objectInputStream.readObject();
System.out.println(savedGame);
objectInputStream.close();
}
}
और यहाँ परिणाम है:
SavedGame{territoriesInfo=null, resourcesInfo=ResourcesInfo{info='Spain has 100 gold, Russia has 80 gold, France has 90 gold'}, diplomacyInfo=DiplomacyInfo{info='France is at war with Russia, Spain has taken a neutral position'}}
इसके अतिरिक्त, हमें अपने प्रश्न का उत्तर मिला कि किसी transient
क्षेत्र को क्या मान दिया जाता है। इसे डिफ़ॉल्ट मान असाइन किया जाता है। वस्तुओं के लिए, यह है null
। जब आपके पास कुछ मिनट शेष हों तो आप क्रमांकन पर इस उत्कृष्ट लेख को पढ़ सकते हैं । यह इंटरफ़ेस का भी उल्लेख करता है Externalizable
, जिसके बारे में हम अगले पाठ में बात करेंगे। इसके अतिरिक्त, "हेड-फर्स्ट जावा" पुस्तक में इस विषय पर एक अध्याय है। इसे थोड़ा ध्यान दें :)