Serializable
त्याचे कार्य केले, आणि संपूर्ण प्रक्रियेच्या स्वयंचलित अंमलबजावणीबद्दल काय आवडत नाही? आणि आम्ही पाहिलेली उदाहरणे देखील जटिल होती. मग अडचण काय आहे? मूलत: समान कार्यांसाठी आम्हाला दुसर्या इंटरफेसची आवश्यकता का आहे? वस्तुस्थिती अशी आहे की त्यात Serializable
अनेक कमतरता आहेत. आम्ही त्यापैकी काही सूचीबद्ध करतो:
-
कामगिरी. इंटरफेसमध्ये
Serializable
अनेक फायदे आहेत, परंतु उच्च कार्यक्षमता स्पष्टपणे त्यापैकी एक नाही.प्रथम,
Serializable
अंतर्गत अंमलबजावणी मोठ्या प्रमाणात सेवा माहिती आणि सर्व प्रकारचा तात्पुरता डेटा व्युत्पन्न करते.दुसरे,
Serializable
रिफ्लेक्शन एपीआयवर अवलंबून आहे (तुम्हाला आत्ता यावर खोलवर जाण्याची गरज नाही; तुम्हाला स्वारस्य असल्यास, तुम्ही तुमच्या आरामात अधिक वाचू शकता). ही गोष्ट तुम्हाला Java मध्ये अशक्य वाटणाऱ्या गोष्टी करू देते: उदाहरणार्थ, खाजगी फील्डची मूल्ये बदला. CodeGym मध्ये Reflection API बद्दल उत्कृष्ट लेख आहे . त्याबद्दल तुम्ही तिथे वाचू शकता. -
लवचिकता. जेव्हा आम्ही इंटरफेस वापरतो तेव्हा आम्ही सीरियलायझेशन-डिसिरियलायझेशन प्रक्रिया नियंत्रित करत नाही
Serializable
.एकीकडे, हे खूप सोयीचे आहे, कारण जर आम्हाला कार्यप्रदर्शनाबद्दल विशेष काळजी नसेल, तर कोड लिहिण्याची गरज नाही हे छान दिसते. पण जर आम्हाला खरोखरच आमची स्वतःची काही वैशिष्ट्ये (आम्ही खाली उदाहरण देऊ) सीरियलायझेशन लॉजिकमध्ये जोडण्याची गरज असेल तर?
transient
मुळात, काही डेटा वगळण्यासाठी आपल्याला प्रक्रिया नियंत्रित करायची आहे . बस एवढेच. तो आमचा संपूर्ण टूलबॉक्स आहे :/ -
सुरक्षा. हा आयटम मागील आयटममधून काही प्रमाणात प्राप्त झाला आहे.
आम्ही याआधी याबद्दल विचार करण्यात जास्त वेळ घालवला नाही, परंतु तुमच्या वर्गातील काही माहिती इतरांच्या डोळ्यांना आणि कानांना उद्देशून नसेल तर? एक साधे उदाहरण म्हणजे पासवर्ड किंवा इतर वैयक्तिक वापरकर्ता डेटा, जो आजच्या जगात अनेक कायद्यांद्वारे शासित आहे.
आम्ही वापरत असल्यास
Serializable
, आम्ही त्याबद्दल खरोखर काहीही करू शकत नाही. आम्ही सर्वकाही जसे आहे तसे क्रमवारी लावतो.परंतु जर आम्ही ते योग्य प्रकारे केले तर, फाइलवर लिहिण्यापूर्वी किंवा नेटवर्कवर पाठवण्यापूर्वी आम्ही या प्रकारचा डेटा एनक्रिप्ट करणे आवश्यक आहे. पण
Serializable
हे शक्य होत नाही.
Externalizable
.
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
public class UserInfo implements Externalizable {
private String firstName;
private String lastName;
private String superSecretInformation;
private static final long SERIAL_VERSION_UID = 1L;
// ...constructor, getters, setters, toString()...
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
जसे आपण पाहू शकता, आमच्याकडे लक्षणीय बदल आहेत! मुख्य एक स्पष्ट आहे: इंटरफेस लागू करताना Externalizable
, आपण दोन आवश्यक पद्धती अंमलात आणल्या पाहिजेत: writeExternal()
आणिreadExternal()
. आम्ही आधी म्हटल्याप्रमाणे, सीरियलायझेशन आणि डीसीरियलायझेशनची जबाबदारी प्रोग्रामरवर असेल. परंतु आता आपण प्रक्रियेवर नियंत्रण नसल्याची समस्या सोडवू शकता! संपूर्ण प्रक्रिया थेट तुमच्याद्वारे प्रोग्राम केलेली आहे. स्वाभाविकच, हे अधिक लवचिक यंत्रणा परवानगी देते. याव्यतिरिक्त, सुरक्षिततेची समस्या सोडवली जाते. जसे तुम्ही बघू शकता, आमच्या वर्गात वैयक्तिक डेटा फील्ड आहे जो एनक्रिप्टेड संग्रहित केला जाऊ शकत नाही. आता ही मर्यादा पूर्ण करणारा कोड आपण सहज लिहू शकतो. उदाहरणार्थ, संवेदनशील डेटा एन्क्रिप्ट आणि डिक्रिप्ट करण्यासाठी आम्ही आमच्या वर्गात दोन सोप्या खाजगी पद्धती जोडू शकतो. आम्ही डेटा फाइलमध्ये लिहू आणि फाइलमधून एनक्रिप्टेड स्वरूपात वाचू. बाकीचा डेटा जसा आहे तसा लिहिला आणि वाचला जाईल :) परिणामी, आमचा वर्ग असे काहीतरी दिसतो:
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Base64;
public class UserInfo implements Externalizable {
private String firstName;
private String lastName;
private String superSecretInformation;
private static final long serialVersionUID = 1L;
public UserInfo() {
}
public UserInfo(String firstName, String lastName, String superSecretInformation) {
this.firstName = firstName;
this.lastName = lastName;
this.superSecretInformation = superSecretInformation;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(this.getFirstName());
out.writeObject(this.getLastName());
out.writeObject(this.encryptString(this.getSuperSecretInformation()));
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
firstName = (String) in.readObject();
lastName = (String) in.readObject();
superSecretInformation = this.decryptString((String) in.readObject());
}
private String encryptString(String data) {
String encryptedData = Base64.getEncoder().encodeToString(data.getBytes());
System.out.println(encryptedData);
return encryptedData;
}
private String decryptString(String data) {
String decrypted = new String(Base64.getDecoder().decode(data));
System.out.println(decrypted);
return decrypted;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
public String getSuperSecretInformation() {
return superSecretInformation;
}
}
आम्ही दोन पद्धती अंमलात आणल्या ज्या समान ObjectOutput
आणि ObjectInput
पॅरामीटर्स वापरतात ज्याबद्दल आम्ही धड्यात आधीच भेटलो होतो Serializable
. योग्य क्षणी, आम्ही आवश्यक डेटा कूटबद्ध किंवा डिक्रिप्ट करतो आणि आम्ही आमच्या ऑब्जेक्टला अनुक्रमित करण्यासाठी एनक्रिप्टेड डेटा वापरतो. हे व्यवहारात कसे दिसते ते पाहूया:
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Username\\Desktop\\save.ser");
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
UserInfo userInfo = new UserInfo("Paul", "Piper", "Paul Piper's passport data");
objectOutputStream.writeObject(userInfo);
objectOutputStream.close();
}
}
encryptString()
आणि पद्धतींमध्ये decryptString()
, ज्या फॉर्ममध्ये गुप्त डेटा लिहिला आणि वाचला जाईल त्याची पडताळणी करण्यासाठी आम्ही विशेषत: कन्सोल आउटपुट जोडले. वरील कोड खालील ओळ प्रदर्शित करते: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh एन्क्रिप्शन यशस्वी झाले! फाइलची संपूर्ण सामग्री यासारखी दिसते: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx आता आमचे डिसिरियलायझेशन लॉजिक वापरून पाहू.
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);
UserInfo userInfo = (UserInfo) objectInputStream.readObject();
System.out.println(userInfo);
objectInputStream.close();
}
}
बरं, इथे काहीही क्लिष्ट दिसत नाही. ते कार्य केले पाहिजे! आम्ही ते चालवतो आणि मिळवतो... "मुख्य" थ्रेडमधील अपवाद java.io.InvalidClassException: UserInfo; कोणतेही वैध कन्स्ट्रक्टर नाही अरेरे! :( वरवर पाहता, हे इतकं सोपं नाही! डीसीरियलायझेशन मेकॅनिझमने अपवाद केला आणि आम्ही डीफॉल्ट कन्स्ट्रक्टर बनवण्याची मागणी केली. मला आश्चर्य वाटलं का. सोबत , Serializable
आम्ही एक न करता पूर्ण केले... :/ येथे आम्हाला आणखी एक महत्त्वाचा मुद्दा समोर आला आहे. फरक फक्त प्रोग्रामरच्या 'विस्तारित' प्रवेशामध्ये आणि प्रक्रियेवर अधिक लवचिकपणे नियंत्रण ठेवण्याच्या क्षमतेमध्येच नाही तर प्रक्रियेतच आहे. सर्वात महत्त्वाचे म्हणजे, Serializable
डिसिरियलायझेशन मेकॅनिझममध्ये . वापरतानाExternalizable
Serializable
, मेमरी फक्त ऑब्जेक्टसाठी वाटप केली जाते, आणि नंतर प्रवाहातून मूल्ये वाचली जातात आणि ऑब्जेक्टची फील्ड सेट करण्यासाठी वापरली जातात. आपण वापरल्यास Serializable
, ऑब्जेक्टच्या कन्स्ट्रक्टरला कॉल केला जात नाही! सर्व कार्य परावर्तनाद्वारे होते (प्रतिबिंब API, ज्याचा आपण शेवटच्या धड्यात थोडक्यात उल्लेख केला आहे). सह Externalizable
, डिसिरियलायझेशन यंत्रणा वेगळी आहे. डीफॉल्ट कन्स्ट्रक्टरला प्रथम म्हटले जाते. त्यानंतरच तयार केलेल्या UserInfo
ऑब्जेक्टची readExternal()
पद्धत म्हणतात. ऑब्जेक्टचे फील्ड सेट करण्यासाठी ते जबाबदार आहे. म्हणूनच इंटरफेसची अंमलबजावणी करणाऱ्या कोणत्याही वर्गामध्ये Externalizable
डीफॉल्ट कन्स्ट्रक्टर असणे आवश्यक आहे . चला आमच्या UserInfo
वर्गात एक जोडू आणि कोड पुन्हा चालवू:
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);
UserInfo userInfo = (UserInfo) objectInputStream.readObject();
System.out.println(userInfo);
objectInputStream.close();
}
}
कन्सोल आउटपुट: पॉल पायपरचा पासपोर्ट डेटा UserInfo \ firstName = 'Paul', lastName = 'Piper', superSecretInformation = 'पॉल पायपरचा पासपोर्ट डेटा' } आता ते पूर्णपणे वेगळे आहे! प्रथम, गुप्त माहितीसह डिक्रिप्ट केलेली स्ट्रिंग कन्सोलवर प्रदर्शित केली गेली. मग आम्ही फाइलमधून पुनर्प्राप्त केलेली वस्तू स्ट्रिंग म्हणून प्रदर्शित केली गेली! म्हणून आम्ही सर्व समस्या यशस्वीरित्या सोडवल्या आहेत :) क्रमवारी आणि डीसीरियलायझेशनचा विषय सोपा वाटतो, परंतु, जसे आपण पाहू शकता, धडे लांब आहेत. आणि आम्ही कव्हर केलेले नाही असे बरेच काही आहे! यापैकी प्रत्येक इंटरफेस वापरताना अजूनही अनेक बारकावे आहेत. परंतु तुमच्या मेंदूला जास्त नवीन माहितीचा स्फोट होऊ नये म्हणून, मी आणखी काही महत्त्वाचे मुद्दे थोडक्यात सूचीबद्ध करेन आणि तुम्हाला अतिरिक्त वाचनासाठी लिंक देईन. तर, आपल्याला आणखी काय माहित असणे आवश्यक आहे? प्रथम , सीरियलायझेशन दरम्यान (तुम्ही वापरत आहात Serializable
किंवा नाही याची पर्वा न करता Externalizable
), व्हेरिएबल्सकडे लक्ष द्या static
. जेव्हा तुम्ही वापरता Serializable
, तेव्हा ही फील्ड अजिबात अनुक्रमित केली जात नाहीत (आणि त्यानुसार, त्यांची मूल्ये बदलत नाहीत, कारण static
फील्ड वर्गाची असतात, ऑब्जेक्टची नाही). पण जेव्हा तुम्ही वापरताExternalizable
, तुम्ही प्रक्रिया स्वतः नियंत्रित करता, त्यामुळे तांत्रिकदृष्ट्या तुम्ही त्यांना अनुक्रमित करू शकता. परंतु, आम्ही याची शिफारस करत नाही, कारण असे केल्याने बरेच सूक्ष्म दोष निर्माण होण्याची शक्यता आहे. दुसरे , तुम्ही मॉडिफायरसह व्हेरिएबल्सकडे देखील लक्ष दिले पाहिजे final
. जेव्हा तुम्ही वापरता तेव्हा Serializable
ते नेहमीप्रमाणे अनुक्रमित आणि डीसीरियलाइज केले जातात, परंतु जेव्हा तुम्ही वापरता तेव्हा व्हेरिएबल Externalizable
डीसीरियल करणे अशक्य आहेfinal
! कारण सोपे आहे: final
जेव्हा डीफॉल्ट कन्स्ट्रक्टरला कॉल केला जातो तेव्हा सर्व फील्ड सुरू होतात — त्यानंतर, त्यांचे मूल्य बदलले जाऊ शकत नाही. म्हणून, फील्ड असलेल्या ऑब्जेक्ट्सचे क्रमिकरण करण्यासाठी final
, द्वारे प्रदान केलेले मानक अनुक्रमिकरण वापरा Serializable
. तिसरे , जेव्हा तुम्ही वारसा वापरता तेव्हा सर्व वंशज वर्ग जे काही वारसा घेतातExternalizable
क्लासमध्ये डीफॉल्ट कन्स्ट्रक्टर असणे आवश्यक आहे. सीरियलायझेशन मेकॅनिझमबद्दल चांगल्या लेखाची लिंक येथे आहे:
पुढच्या वेळे पर्यंत! :)
GO TO FULL VERSION