Serializable
Мен өзүмдүн жумушумду аткардым жана бүт процесстин автоматтык түрдө ишке ашырылышы кубанбай койбойт. Биз карап чыккан мисалдар да татаал эмес болчу. Анда эмне болгон? Эмне үчүн бир эле тапшырма үчүн башка интерфейс керек? Serializable
Анын бир катар кемчorктери бар экендиги чындык . Алардын айрымдарын санап көрөлү:
-
Performance. Интерфейстин
Serializable
көптөгөн артыкчылыктары бар, бирок жогорку аткаруу алардын бири эмес.
Биринчиден , ички механизм Serializable
иш учурунда кызматтык маалыматтын жана ар кандай түрдөгү убактылуу маалыматтардын чоң көлөмүн түзөт.
Экинчиден (эгер сизди кызыктырса, азыр ага кирип, бош убактыңызда окуунун кереги жок), иш Serializable
Reflection API колдонууга негизделген. Бул карама-каршылык Javaда мүмкүн эместей көрүнгөн нерселерди кылууга мүмкүндүк берет: мисалы, жеке талаалардын баалуулуктарын өзгөртүү. JavaRush Reflection API жөнүндө эң сонун макалага ээ , сиз бул тууралуу окуй аласыз.
-
ийкемдүүлүк. Колдонууда сериялаштыруу-десериялаштыруу процессин такыр көзөмөлдөбөйбүз
Serializable
.Бир жагынан алганда, бул абдан ыңгайлуу, анткени биз аткарууга чындап маани бербесек, codeду жазбай коюу ыңгайлуу көрүнөт. Бирок сериялаштыруу логикасына чындап эле өзүбүздүн айрым өзгөчөлүктөрүбүздү (алардын биринин мисалы төмөндө болот) кошуу керек болсочу?
Негизинен, процессти көзөмөлдөөбүз керек болгон нерсе - бул
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;
//...конструктор, геттеры, сеттеры, toString()...
@Override
public void writeExternal(ObjectOutput out) throws IOException {
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
}
}
Көрүнүп тургандай, биз олуттуу өзгөртүүлөрдү киргиздик! Эң негизгиси көрүнүп турат: интерфейсти ишке ашырууда Externalizable
эки милдеттүү ыкманы ишке ашыруу керек - writeExternal()
жана readExternal()
. Мурда айткандай, сериалдаштыруу жана сериялаштыруу үчүн бардык жоопкерчorк программистке жүктөлөт. Бирок, азыр бул процесске контролдуктун жоктугун чечсе болот! Бүт процесс түздөн-түз сиз тарабынан программаланган, бул, албетте, бир топ ийкемдүү механизмди түзөт. Мындан тышкары, коопсуздук маселеси да чечилди. Көрүнүп тургандай, биздин класста бир талаа бар: шифрлөөсүз сакталган жеке маалыматтар. Эми биз бул чектөөгө жооп берген codeду оңой жаза алабыз. Мисалы, жашыруун маалыматтарды шифрлөө жана чечмелөө үчүн биздин класска эки жөнөкөй жеке ыкманы кошуңуз. Биз аларды файлга жазып, шифрленген түрдө файлдан окуйбуз. Ал эми калган маалыматтарды ошол бойдон жазабыз жана окуйбуз :) Натыйжада, биздин класс төмөнкүдөй болот:
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 out
жана параметрлерди колдонгон эки ыкманы ишке ашырдык . Керектүү учурда биз керектүү маалыматтарды шифрлейбиз же чечмелейбиз жана бул формада биз аны an objectибизди сериялаштыруу үчүн колдонобуз. Келгиле, бул иш жүзүндө кандай болорун карап көрөлү: 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("Ivan", "Ivanov", "Ivan Ivanov's passport data");
objectOutputStream.writeObject(userInfo);
objectOutputStream.close();
}
}
encryptString()
жана методдорунда decryptString()
биз жашыруун маалыматтар кандай формада жазылып жана окула турганын текшерүү үчүн консолго атайын чыгардык. Жогорудагы code консолго төмөнкү сапты чыгарат: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Шифрлөө ийгorктүү болду! Файлдын толук мазмуну төмөнкүдөй көрүнөт: ¬н 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();
}
}
Ооба, бул жерде татаал эч нерсе жок окшойт, иштеши керек! Иштейли... "main" жипиндеги өзгөчө жагдай java.io.InvalidClassException: UserInfo; жарактуу конструктор жок Ой :( Бул анчалык деле жөнөкөй эмес болуп чыкты! Сериялаштыруу механизми өзгөчө кырдаалды жаратып, бизден демейки конструкторду түзүүнү талап кылды. Кызык, эмнеге? Serializable
Биз ансыз да жетиштик... :/ Бул жерде биз дагы бир маанилүү нюанска келдик. Serializable
Алардын ортосундагы айырмачылык Externalizable
программалоочу үчүн “кеңейтилген” жеткorктүүлүктө жана процессти ийкемдүү башкаруу мүмкүнчүлүгүнөн гана эмес, процесстин өзүндө да.Биринчиден, сериядан чыгаруу механизминде ... Колдонулганда, Serializable
эс жөн эле an object үчүн бөлүнгөн, андан кийин анын бардык талааларын толтурган агымдан баалуулуктар окулат. Эгерде биз колдонсок Serializable
, an objectтин конструктору чакырылbyte! Бардык иштер чагылдыруу аркылуу аткарылат (Reflection API, биз акыркы бөлүмдө кыскача айтып өткөнбүз. лекция).Жагдайда Externalizable
сериядан чыгаруу механизми башкача болот. Башында демейки конструктор чакырылат. Ошондон кийин гана an objectтин талааларын толтуруу үчүн жооп берген UserInfo
түзүлгөн an object ыкмасына чакырылат . Башкача айтканда. readExternal()
эмне үчүн интерфейсти ишке ашырган класстын Externalizable
демейки конструктору болушу керек . Аны классыбызга кошуп UserInfo
, codeду кайра иштетели:
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();
}
}
Консолдун чыгышы: Ivan Ivanовдун паспорттук маалыматтары UserInfo{firstName='Ivan', lastName='Ivanov', superSecretInformation='Ivan Ivanовдун паспорттук маалыматтары'} Такыр башка маселе! Биринчиден, жашыруун маалыматтар менен шифрленген сап консолго чыгарылды, андан кийин биздин an object файлдан сап форматында калыбына келтирилди! Мына ушинтип биз бардык көйгөйлөрдү ийгorктүү чечтик :) Сериялаштыруу жана сериялаштыруу темасы жөнөкөй көрүнөт, бирок өзүңүздөр көрүп тургандай, биздин лекциялар узак болуп чыкты. Жана бул баары эмес! Бул интерфейстердин ар бирин колдонууда дагы көптөгөн кылдаттыктар бар, бирок азыр сиздин мээңиз жаңы маалыматтын көлөмүнөн жарылбашы үчүн, мен дагы бир нече маанилүү ойлорду кыскача тизмелеп, кошумча окууга шилтеме берем. Демек, дагы эмнени бorшиңиз керек? БиринчиденSerializable
, сериялаштырууда ( колдонгонуңуз маанилүү эмес Externalizable
), өзгөрмөлөргө көңүл буруңуз static
. Колдонулганда Serializable
бул талаалар такыр серияланbyte (жана, ошого жараша алардын мааниси өзгөрбөйт, анткени static
талаалар an objectке эмес, класска таандык). Бирок аны колдонууда Externalizable
процессти өзүңүз башкарасыз, андыктан техникалык жактан муну жасоого болот. Бирок бул сунушталbyte, анткени бул тымызын каталар менен коштолот. Экинчиден , модификатору бар өзгөрмөлөргө да көңүл буруу керек final
. Колдонулганда, Serializable
алар адаттагыдай сериялаштырылат жана сериядан ажыратылат, бирок колдонулганда, өзгөрмөнүн Externalizable
сериясын жок кылууfinal
мүмкүн эмес ! Себеби жөнөкөй: final
демейки конструктор чакырылганда бардык талаалар инициализацияланат жана андан кийин алардын маанисин өзгөртүүгө болбойт. Ошондуктан, final
-талааларды камтыган an objectтерди сериялаштыруу үчүн, аркылуу стандарттуу сериялаштыруу колдонуңуз Serializable
. Үчүнчүдөн , мурасты колдонууда, кандайдыр бир Externalizable
класстан келип чыккан бардык тукум кууруучу класстарда демейки конструкторлор да болушу керек. Бул жерде сериалдаштыруу механизмдери жөнүндө жакшы макалаларга шилтемелер бар:
Көрүшкөнчө! :)
GO TO FULL VERSION