Salam! Bu gün biz Java obyektlərinin seriallaşdırılması və sıradan çıxarılması ilə bağlı təqdimatımızı davam etdirəcəyik. Son mühazirədə biz Serializable marker interfeysi ilə tanış olduq , onun istifadə nümunələrinə baxdıq, həmçinin keçid açar sözündən istifadə edərək seriallaşdırma prosesini idarə etməyi öyrəndik . Yaxşı, "prosesi idarə et" əlbəttə ki, güclü bir sözdür. Bizim bir açar sözümüz, bir versiya identifikatorumuz var və bu, əslində. Prosesin qalan hissəsi Java daxilində “sabitdir” və ona giriş yoxdur. Rahatlıq baxımından bu, əlbəttə ki, yaxşıdır. Ancaq bir proqramçı öz işində təkcə öz rahatlığına diqqət etməməlidir, elə deyilmi? :) Nəzərə alınacaq başqa amillər də var. Buna görə də, Serializable Java-da serializasiya-deserializasiya üçün yeganə alət deyil. Bu gün biz Externalizable interfeysi ilə tanış olacağıq . Ancaq biz onu öyrənməyə başlamazdan əvvəl belə bir ağlabatan sualınız ola bilər: niyə bizə başqa vasitə lazımdır? 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, Serializableonun bir sıra mənfi cəhətləri var. Onlardan bəzilərini sadalayaq:
  1. Performans. İnterfeys Serializablebir çox üstünlüklərə malikdir, lakin yüksək performans açıq şəkildə onlardan biri deyil.

Xariciləşdirilə bilən interfeysin təqdimatı - 2

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ş SerializableReflection 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.

  1. Ç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 transientbəzi məlumatları istisna etmək üçün açar sözdür, vəssalam. Bir növ "alət dəsti" kimi :/

  2. 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 Serializablebu imkanı vermir.

Xariciləşdirilə bilən interfeysin təqdimatı - 3Nəhayət, görək sinif istifadə edərək necə görünəcək 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 Externalizableiki məcburi üsul tətbiq etməlisiniz - writeExternal()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 outvə 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: ObjectInputSerializable
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 Xariciləşdirilə bilən interfeysin təqdimatı - 4 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ə? SerializableOnsuz da bacardıq... :/ Burada başqa bir vacib nüansa gəldik. SerializableArasındakı fərq Externalizableproqramçı üçü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ə Serializableyaddaş 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ə Externalizablesı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 UserInfoyaradılmış obyekt metodu çağırılır.Yəni . readExternal()niyə interfeysi həyata keçirən hər hansı bir sinifin Externalizablestandart konstruktoru olmalıdır . Gəlin onu sinfimizə əlavə edək UserInfovə 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ə, Serializablebu sahələr ümumiyyətlə seriyalaşdırılmır (və müvafiq olaraq, onların dəyəri dəyişmir, çünki staticsahələr obyektə deyil, sinifə aiddir). Ancaq ondan istifadə edərkən Externalizableprosesi ö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ə, Serializableonlar həmişəki kimi seriallaşdırılır və seriyadan çıxarılır, lakin istifadə edildikdə, dəyişəni Externalizablesıradan çıxarmaqfinal mümkün deyil ! Səbəb sadədir: finalstandart 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 finalvasitəsilə standart seriallaşdırmadan istifadə edin Serializable. Üçüncüsü , mirasdan istifadə edərkən, hansısa Externalizablesinifdə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! :)