हाय! आजच्या धड्यात, आपण जावा मधील सीरियलायझेशन आणि डीसीरियलायझेशनबद्दल बोलू. आपण एका साध्या उदाहरणाने सुरुवात करू. समजा तुम्ही संगणक गेम तयार केला आहे. जर तुम्ही ९० च्या दशकात मोठे झाले असाल आणि त्या काळातील गेम कन्सोल आठवत असाल, तर तुम्हाला कदाचित माहित असेल की त्यांच्यात काहीतरी कमी आहे जे आज आम्ही गृहीत धरतो — गेम जतन करण्याची आणि लोड करण्याची क्षमता :) नसल्यास, याची कल्पना करा! मला भीती वाटते की आज या क्षमतेशिवाय खेळ नशिबात येईल! तरीही गेम "जतन करणे" आणि "लोड करणे" म्हणजे काय? बरं, आम्हाला सामान्य अर्थ समजतो: आम्ही जिथे सोडला होता तिथून आम्हाला खेळ सुरू ठेवायचा आहे. हे करण्यासाठी, आम्ही एक प्रकारचा "चेक पॉइंट" तयार करतो, जो आम्ही गेम लोड करण्यासाठी वापरतो. पण कॅज्युअल गेमरपेक्षा प्रोग्रामरला याचा अर्थ काय आहे? उत्तर सोपे आहे: आम्ही'. समजा तुम्ही स्ट्रॅटेजियममध्ये स्पेन म्हणून खेळत आहात. तुमच्या खेळाचे एक राज्य आहे: कोणते प्रदेश कोणाच्या मालकीचे आहेत, कोणाकडे किती संसाधने आहेत, कोण कोणाशी युती आहे, कोण कोणाशी युद्ध करत आहे इ. भविष्यात ती पुनर्संचयित करण्यासाठी आणि गेम सुरू ठेवण्यासाठी आम्ही ही माहिती, आमच्या प्रोग्रामची स्थिती जतन केली पाहिजे. सीरियलायझेशन आणि डिसिरियलायझेशन हे नेमके कशासाठी आहे. सीरियलायझेशन ही बाइट्सच्या क्रमाने ऑब्जेक्टची स्थिती संग्रहित करण्याची प्रक्रिया आहे. डिसिरियलायझेशनया बाइट्समधून ऑब्जेक्ट पुनर्संचयित करण्याची प्रक्रिया आहे. कोणतीही Java ऑब्जेक्ट बाइट अनुक्रमात रूपांतरित केले जाऊ शकते. आम्हाला याची गरज का असेल? आम्ही एकापेक्षा जास्त वेळा सांगितले आहे की प्रोग्राम स्वतःच अस्तित्वात नाहीत. बर्याचदा, ते इतर प्रोग्राम्सशी संवाद साधतात, डेटाची देवाणघेवाण करतात. आणि बाइट अनुक्रम एक सोयीस्कर आणि कार्यक्षम स्वरूप आहे. उदाहरणार्थ, आम्ही आमच्या
SavedGame
ऑब्जेक्टला बाइट्सच्या अनुक्रमात बदलू शकतो, हे बाइट्स नेटवर्कवरून दुसर्या संगणकावर पाठवू शकतो आणि नंतर दुसऱ्या संगणकावर हे बाइट्स परत Java ऑब्जेक्टमध्ये बदलू शकतो! कठीण वाटतं, बरोबर? आणि ही प्रक्रिया अंमलात आणणे एक वेदनासारखे वाटते :/ आनंदाने, असे नाही! :) जावा मध्ये, द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) +
'}';
}
}
प्रदेश, संसाधने आणि मुत्सद्दीपणाबद्दल माहितीसाठी तीन अॅरे जबाबदार आहेत. सीरिअलायझ करण्यायोग्य इंटरफेस जावा व्हर्च्युअल मशीनला सांगतो: " सर्व काही ठीक आहे — आवश्यक असल्यास, या वर्गाच्या वस्तू अनुक्रमित केल्या जाऊ शकतात ". एका इंटरफेसशिवाय इंटरफेस विचित्र दिसत आहे :/ ते का आवश्यक आहे? या प्रश्नाचे उत्तर वर पाहिले जाऊ शकते: ते फक्त Java आभासी मशीनला आवश्यक माहिती प्रदान करते. आमच्या मागील धड्यांपैकी एकामध्ये, आम्ही मार्कर इंटरफेसचा थोडक्यात उल्लेख केला आहे . हे विशेष माहितीचे इंटरफेस आहेत जे आमच्या वर्गांना फक्त अतिरिक्त माहितीसह चिन्हांकित करतात जे भविष्यात Java मशीनसाठी उपयुक्त ठरतील. त्यांच्याकडे तुम्हाला अंमलात आणण्याच्या कोणत्याही पद्धती नाहीत.Serializable
त्या इंटरफेसपैकी एक आहे. आणखी एक महत्त्वाचा मुद्दा: private static final long serialVersionUID
आपण वर्गात परिभाषित केलेल्या व्हेरिएबलची आपल्याला आवश्यकता का आहे? त्याची गरज का आहे? या फील्डमध्ये अनुक्रमित वर्गाच्या आवृत्तीसाठी एक अद्वितीय अभिज्ञापक आहे . इंटरफेस लागू करणार्या कोणत्याही वर्गाला Serializable
एक अभिज्ञापक असतो version
. त्याची गणना वर्गातील सामग्रीच्या आधारे केली जाते: त्याचे फील्ड, ते ज्या क्रमाने घोषित केले जातात, पद्धती इ. जर आम्ही फील्डचा प्रकार आणि/किंवा आमच्या वर्गातील फील्डची संख्या बदलली, तर आवृत्ती अभिज्ञापक त्वरित बदलतो. . serialVersionUID
वर्ग क्रमवारीत असताना देखील लिहिले जाते. जेव्हा आपण डिसिरियलाइज करण्याचा प्रयत्न करतो, म्हणजे, बाइट्सच्या संचामधून ऑब्जेक्ट पुनर्संचयित करतो, तेव्हा त्याच्याशी संबंधित वस्तूची serialVersionUID
तुलना मूल्याशी केली जाते.serialVersionUID
आमच्या कार्यक्रमातील वर्गासाठी. जर मूल्ये जुळत नसतील, तर java.io. InvalidClassException टाकले जाईल. याचे उदाहरण आपण खाली पाहू. हे टाळण्यासाठी, आम्ही आमच्या वर्गात व्हर्जन आयडेंटिफायर मॅन्युअली सेट करतो. आमच्या बाबतीत, ते फक्त 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
आम्ही वर उल्लेख केलेला हा अपवाद आहे. तसे, आम्ही काहीतरी महत्त्वाचे गमावले. हे समजते की स्ट्रिंग्स आणि प्रिमिटिव्ह सहजपणे अनुक्रमित केले जाऊ शकतात: हे करण्यासाठी Java मध्ये कदाचित काही प्रकारचे अंगभूत यंत्रणा आहे. पण जर आपल्या 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
वर्ग नॉन-सिरिअलायझेबल होईल! ही एक समस्या आहे :/ Java मध्ये, कीवर्ड वापरून अशा प्रकारच्या समस्या सोडवल्या जातात 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
, ज्याबद्दल आपण पुढील धड्यात बोलू. याव्यतिरिक्त, "हेड-फर्स्ट जावा" या पुस्तकात या विषयावर एक अध्याय आहे. जरा लक्ष द्या :)
GO TO FULL VERSION