Serializable
İşimin öhdəsindən gəldim və bütün prosesin avtomatik həyata keçirilməsi sevinməyə bilməz. Baxdığımız nümunələr də mürəkkəb deyildi. Yaxşı, nə iş var? Niyə mahiyyətcə eyni tapşırıq üçün başqa bir interfeys? Fakt budur ki, Serializable
onun bir sıra mənfi cəhətləri var. Onlardan bəzilərini sadalayaq:
-
Performans. İnterfeys
Serializable
bir çox üstünlüklərə malikdir, lakin yüksək performans açıq şəkildə onlardan biri deyil.
Birincisi , daxili mexanizm Serializable
əməliyyat zamanı böyük miqdarda xidmət məlumatı və müxtəlif növ müvəqqəti məlumat yaradır.
İkincisi (indi buna girib, maraqlanırsınızsa, asudə vaxtınızda oxumağa ehtiyac yoxdur), iş Serializable
Reflection API istifadəsinə əsaslanır. Bu ziddiyyət Java-da qeyri-mümkün görünən şeyləri etməyə imkan verir: məsələn, şəxsi sahələrin dəyərlərini dəyişdirin. JavaRush-un Reflection API haqqında əla məqaləsi var , bu barədə burada oxuya bilərsiniz.
-
Çeviklik. istifadə edərkən serializasiya-deserializasiya prosesinə ümumiyyətlə nəzarət etmirik
Serializable
.Bir tərəfdən, bu çox rahatdır, çünki performansa əhəmiyyət vermiriksə, kod yazmamaq qabiliyyəti rahat görünür. Bəs həqiqətən seriallaşdırma məntiqinə öz xüsusiyyətlərimizdən bəzilərini (onlardan birinin nümunəsi aşağıda göstəriləcək) əlavə etməliyiksə?
Əslində, prosesə nəzarət etməli olduğumuz yeganə şey
transient
bəzi məlumatları istisna etmək üçün açar sözdür, vəssalam. Bir növ "alət dəsti" kimi :/ -
Təhlükəsizlik. Bu məqam qismən əvvəlkindən irəli gəlir.
Əvvəllər bu barədə çox düşünməmişik, bəs sizin sinifinizdəki bəzi məlumatlar “başqalarının qulaqları” (daha doğrusu, gözləri) üçün nəzərdə tutulmayıbsa necə? Sadə bir nümunə, müasir dünyada bir dəstə qanunla tənzimlənən istifadəçinin parolu və ya digər şəxsi məlumatlarıdır.
istifadə edərək
Serializable
, əslində bu barədə heç nə edə bilmərik. Hər şeyi olduğu kimi seriallaşdırırıq.Ancaq yaxşı mənada, bu cür məlumatları fayla yazmadan və ya şəbəkə üzərindən ötürməzdən əvvəl şifrələməliyik. Amma
Serializable
bu imkanı vermir.
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 {
}
}
Gördüyünüz kimi, biz əhəmiyyətli dəyişikliklər etdik! Əsas odur ki, bir interfeys tətbiq edərkən Externalizable
iki məcburi üsul tətbiq etməlisiniz - writeExternal()
və readExternal()
. Daha əvvəl dediyimiz kimi, seriallaşdırma və seriyadan çıxarma üçün bütün məsuliyyət proqramçının üzərinə düşür. Ancaq indi bu prosesə nəzarətin olmaması problemini həll edə bilərsiniz! Bütün proses birbaşa sizin tərəfinizdən proqramlaşdırılır və bu, əlbəttə ki, daha çevik mexanizm yaradır. Bundan əlavə, təhlükəsizlik problemi də həll edilir. Gördüyünüz kimi, bizim sinifimizdə bir sahə var: şifrəsiz saxlanıla bilməyən şəxsi məlumatlar. İndi bu məhdudiyyətə cavab verən kodu asanlıqla yaza bilərik. Məsələn, gizli məlumatların şifrələnməsi və deşifrə edilməsi üçün sinifimizə iki sadə özəl metod əlavə edin. Biz onları fayla yazacağıq və şifrələnmiş formada fayldan oxuyacağıq. Biz isə qalan məlumatları olduğu kimi yazıb oxuyacağıq :) Nəticədə sinifimiz belə görünəcək:
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;
}
}
Haqqında mühazirədə artıq qarşılaşdığımız eyni ObjectOutput out
və parametrlərdən istifadə edən iki metodu tətbiq etdik . Lazımi vaxtda biz lazımi məlumatları şifrələyirik və ya deşifrə edirik və bu formada obyektimizi seriallaşdırmaq üçün istifadə edirik. Bunun praktikada necə görünəcəyini görək: 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()
və üsullarında decryptString()
gizli məlumatların hansı formada yazılacağını və oxunacağını yoxlamaq üçün konsola xüsusi olaraq çıxış əlavə etdik. Yuxarıdakı kod konsola aşağıdakı sətri verir: SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRh Şifrələmə uğurlu oldu! Faylın tam məzmunu belə görünür: ¬н sr UserInfoГ!}ҐџC‚ћ xpt Ivant Ivanovt $SXZhbiBJdmFub3YncyBwYXNzcG9ydCBkYXRhx İndi yazdığımız sıradan çıxarma məntiqindən istifadə etməyə çalışaq.
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();
}
}
Yaxşı, burada mürəkkəb bir şey yoxdur, işləməlidir! Çalışaq... "main" mövzuda istisna java.io.InvalidClassException: UserInfo; etibarlı konstruktor yoxdur Oops :( O qədər də sadə olmadığı ortaya çıxdı! Deserializasiya mexanizmi bir istisna atdı və bizdən defolt konstruktor yaratmağı tələb etdi. Görəsən niyə? Serializable
Onsuz da bacardıq... :/ Burada başqa bir vacib nüansa gəldik. Serializable
Arasındakı fərq Externalizable
proqramçı üçün “genişlənmiş” giriş və prosesi daha çevik idarə etmək bacarığı ilə yanaşı, həm də prosesin özündədir.İlk növbədə, seriyadan çıxarma mexanizmində ... İstifadə edildikdə Serializable
yaddaş sadəcə olaraq obyekt üçün ayrılır, bundan sonra onun bütün sahələrini dolduran axından dəyərlər oxunur. . Əgər istifadə etsək Serializable
, obyekt konstruktoru çağırılmır! Bütün işlər əks etdirmə yolu ilə həyata keçirilir (Reflection API, axırıncı hissədə qısaca qeyd etdiyimiz). mühazirə).- vəziyyətində Externalizable
sıradan çıxarma mexanizmi fərqli olacaq.Başlanğıcda standart konstruktor çağırılır.Və yalnız bundan sonra obyektin sahələrinin doldurulmasına cavabdeh olan UserInfo
yaradılmış obyekt metodu çağırılır.Yəni . readExternal()
niyə interfeysi həyata keçirən hər hansı bir sinifin Externalizable
standart konstruktoru olmalıdır . Gəlin onu sinfimizə əlavə edək UserInfo
və kodu yenidən işlədək:
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();
}
}
Konsol çıxışı: İvan İvanovun pasport məlumatları UserInfo{firstName='İvan', lastName='İvanov', superSecretInformation='İvan İvanovun pasport məlumatları'} Tamamilə fərqli bir məsələ! Əvvəlcə gizli məlumatlarla şifrələnmiş sətir konsola çıxarıldı və sonra obyektimiz string formatında fayldan bərpa edildi! Bütün problemləri belə uğurla həll etdik :) Serializasiya və serializasiya mövzusu sadə görünsə də, gördüyünüz kimi mühazirələrimiz uzun oldu. Və bu hamısı deyil! Bu interfeyslərin hər birini istifadə edərkən daha çox incəliklər var, lakin indi beyninizin yeni məlumatların həcmindən partlamaması üçün daha bir neçə vacib məqamı qısaca sadalayacağam və əlavə oxumaq üçün keçidlər təqdim edəcəyəm. Beləliklə, başqa nə bilmək lazımdır? BirincisiSerializable
, seriallaşdırarkən (istifadə etməyiniz və ya fərq etməz Externalizable
), dəyişənlərə diqqət yetirin static
. İstifadə edildikdə, Serializable
bu sahələr ümumiyyətlə seriyalaşdırılmır (və müvafiq olaraq, onların dəyəri dəyişmir, çünki static
sahələr obyektə deyil, sinifə aiddir). Ancaq ondan istifadə edərkən Externalizable
prosesi özünüz idarə edirsiniz, buna görə də texniki olaraq bunu etmək olar. Ancaq tövsiyə edilmir, çünki bu, incə səhvlərlə doludur. İkincisi , dəyişdirici ilə dəyişənlərə də diqqət yetirilməlidir final
. İstifadə edildikdə, Serializable
onlar həmişəki kimi seriallaşdırılır və seriyadan çıxarılır, lakin istifadə edildikdə, dəyişəni Externalizable
sıradan çıxarmaqfinal
mümkün deyil ! Səbəb sadədir: final
standart konstruktor çağırıldıqda bütün -sahələr işə salınır və bundan sonra onların dəyəri dəyişdirilə bilməz. Buna görə də, -sahələri olan obyektləri seriallaşdırmaq üçün final
vasitəsilə standart seriallaşdırmadan istifadə edin Serializable
. Üçüncüsü , mirasdan istifadə edərkən, hansısa Externalizable
sinifdən gələn bütün irsi siniflərin standart konstruktorları da olmalıdır. Serializasiya mexanizmləri haqqında yaxşı məqalələrə bəzi keçidlər:
görüşənədək! :)
GO TO FULL VERSION