1. Giriş
Java-da obyektlərin seriyalaşdırılması üçün ən çox Serializable interfeysindən istifadə olunur. O, sadədir: interfeysi reallaşdırmaq kifayətdir və obyekti ObjectOutputStream/ObjectInputStream vasitəsilə yazmaq/oxumaq olar. Lakin bəzən bu kifayət etmir:
- Hansı sahələrin və necə seriyalaşdırılacağına tam nəzarət lazımdır.
- Sinfin müxtəlif versiyaları arasında uyğunluq təmin edilməlidir.
- Seriyalaşdırılmış faylın ölçüsünü azaltmaq və ya prosesi sürətləndirmək vacibdir.
Belə hallarda Java-da Externalizable interfeysi — daha «əl ilə» və çevik seriyalaşdırma üsuludur.
Qısaca:
- Serializable — avtomatik seriyalaşdırma: nəyi və necə yazmağı Java özü həll edir.
- Externalizable — əl ilə seriyalaşdırma: nəyi və necə saxlayıb/bərpa edəcəyinizi siz müəyyən edirsiniz.
2. Externalizable müqaviləsi: writeExternal və readExternal reallaşdırılır
Externalizable-dan istifadə etmək üçün:
- java.io.Externalizable interfeysini reallaşdırın.
- Mütləq iki metodu reallaşdırın:
- void writeExternal(ObjectOutput out) throws IOException
- void readExternal(ObjectInput in) throws IOException, ClassNotFoundException
Nümunə:
import java.io.*;
public class User implements Externalizable {
private String name;
private int age;
// Mütləq public parametrsiz konstruktor!
public User() {}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt();
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
Vacibdir: hansı sahələrin və hansı ardıcıllıqla seriyalaşdırılacağına developer özü qərar verir. Lakin bir məcburi tələb var: sinifin public parametrsiz konstruktoru olmalıdır. Əgər o yoxdursa, deserializasiya zamanı proqram InvalidClassException atacaq.
3. Externalizable nə zaman istifadə olunmalıdır?
Externalizable-dan istifadə edin, əgər:
- Məlumat formatına tam nəzarət lazımdır. Məsələn, yalnız bəzi sahələri seriyalaşdırmaq və ya onları xüsusi ardıcıllıqda/formatda yazmaq istəyirsiniz.
- Performans və fayl ölçüsünün optimallaşdırılması. Standart seriyalaşdırma xidmət məlumatı (metaməlumat, sinif adları, tiplər və s.) əlavə edir. Externalizable ilə siz yalnız lazım olan məlumatları yazırsınız.
- Geriyə uyğunluğun təmin edilməsi. Sinfin strukturu dəyişərsə, köhnə və yeni versiyaların məlumatlarını oxumaq üçün məntiqi əl ilə qura bilərsiniz.
- Qeyri-standart obyektlərin seriyalaşdırılması. Məsələn, standart üsulla seriyalaşdırılması mümkün olmayan sahələriniz varsa (məsələn, transient, volatile və ya mürəkkəb strukturlar).
Nə zaman istifadə ETMƏMƏK lazımdır?
- Əgər sizə tam nəzarət lazım deyilsə — Serializable-dan istifadə edin, bu daha sadə və təhlükəsizdir.
- Sinif dəyişiklikləri zamanı məlumat formatının uyğunluğunu saxlaya biləcəyinizdən əmin deyilsinizsə.
4. Externalizable-ın Serializable ilə müqayisədə üstünlükləri və çatışmazlıqları
Üstünlüklər:
- Seriyalaşdırmaya tam nəzarət. Nəyi və necə yazıb/oxuyacağınızı siz müəyyən edirsiniz.
- Kompaktlıq. Artıq metaməlumat yoxdur — yalnız sizin məlumatlarınız.
- Sürət. Məlumat azdırsa — yazma/oxuma daha sürətlidir.
- Çeviklik. Müxtəlif format versiyalarını dəstəkləmək, sıxılma, şifrələmə və s. əlavə etmək olar.
Çatışmazlıqlar:
- Əl ilə reallaşdırma — səhv etmək asandır. Yazma/oxuma ardıcıllığını səhv etsəniz, seriyalaşdırma «pozular» (xəta və ya yanlış məlumatlar olacaq).
- Avtomatik dəstək yoxdur transient, serialVersionUID. Hər şeyi əvvəlcədən düşünmək və əl ilə reallaşdırmaq lazımdır.
- Dəstəkləmək daha çətindir. Sinifin strukturu dəyişəndə seriyalaşdırma metodlarını yeniləməyi unutmaq olmaz.
- Public parametrsiz konstruktor mütləqdir.
- Daha az «sehr» — daha çox məsuliyyət.
5. Nümunələr: sadə obyektin seriyalaşdırılması və deserializasiyası
Obyektin seriyalaşdırılması
User user = new User("Alice", 30);
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("user.bin"))) {
out.writeObject(user);
}
Obyektin deserializasiyası
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("user.bin"))) {
User loaded = (User) in.readObject();
System.out.println(loaded); // Alice (30)
}
Diqqət: əgər sahələrin yazılma/oxunma ardıcıllığını dəyişsəniz və ya hansısa sahəni seriyalaşdırmağı unutmusunuzsa, məlumatlar düzgün olmayacaq! writeExternal və readExternal metodları əməliyyatların ardıcıllığı baxımından ciddi şəkildə uzlaşdırılmalıdır.
Nümunə: sahələrin yalnız bir hissəsini seriyalaşdıraq
public class SecretUser implements Externalizable {
private String login;
private transient String password; // transient Externalizable üçün əhəmiyyət kəsb etmir
public SecretUser() {}
public SecretUser(String login, String password) {
this.login = login;
this.password = password;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(login);
// Şifrəni seriyalaşdırmırıq!
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
login = in.readUTF();
password = null; // şifrəni bərpa etmirik
}
}
6. Təcrübə: seriyalaşdırılmış faylın ölçüsünü müqayisə etmək
Gəlin Serializable və Externalizable vasitəsilə seriyalaşdırılan faylların «çəki»sini müqayisə edək.
Serializable ilə sinif
public class UserSerializable implements Serializable {
private String name;
private int age;
public UserSerializable(String name, int age) {
this.name = name;
this.age = age;
}
}
Externalizable ilə sinif
public class UserExternalizable implements Externalizable {
private String name;
private int age;
public UserExternalizable() {}
public UserExternalizable(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(name);
out.writeInt(age);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
name = in.readUTF();
age = in.readInt();
}
}
Müqayisə üçün kod
import java.io.*;
public class CompareSerialization {
public static void main(String[] args) throws Exception {
UserSerializable s = new UserSerializable("Bob", 25);
UserExternalizable e = new UserExternalizable("Bob", 25);
// Serializable
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ser.bin"))) {
out.writeObject(s);
}
// Externalizable
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("ext.bin"))) {
out.writeObject(e);
}
System.out.println("Serializable file size: " + new File("ser.bin").length());
System.out.println("Externalizable file size: " + new File("ext.bin").length());
}
}
Nəticə:
ser.bin (Serializable) faylı adətən daha böyükdür — Java-nın xidmət məlumatını ehtiva edir. ext.bin (Externalizable) faylı isə yalnız sizin məlumatlarınızdır və adətən daha kiçik olur.
7. Externalizable ilə işləyərkən tipik səhvlər
Səhv №1: Public parametrsiz konstruktorun olmaması.
Externalizable reallaşdıran sinifin mütləq public arqumentsiz konstruktoru olmalıdır. O olmadıqda deserializasiya InvalidClassException atacaq.
Səhv №2: Sahələrin yazılma və oxunma ardıcıllığının pozulması.
writeExternal və readExternal metodları eyni ardıcıllıqda işləməlidir. Əgər yazarkən əvvəlcə name sahəsini saxlayıb, oxuyarkən əvvəl age sahəsini oxumağa cəhd etsəniz, məlumatlar təhrif olunacaq.
Səhv №3: Seriyalaşdırma zamanı sahələrin buraxılması.
Əgər writeExternal-da sahəni yazmağı unutmusunuzsa, deserializasiya zamanı o, null (istinad tipləri üçün) və ya 0 (rəqəm tipləri üçün) olacaq.
Səhv №4: transient və ya serialVersionUID-in səhv istifadəsi.
Serializable-dan fərqli olaraq, Externalizable üçün bu mexanizmlər avtomatik işləmir — hansı sahələrin saxlanılıb-saxlanılmayacağını siz özünüz idarə etməlisiniz.
Səhv №5: Sinif strukturunu dəyişdirib metodları yeniləməmək.
Sahələr əlavə edib/sildikdə və uyğun dəyişiklikləri writeExternal və readExternal-a tətbiq etmədikdə, köhnə saxlanılmış məlumatlar düzgün yüklənməyə bilər.
GO TO FULL VERSION