1. Serializable interfeysi
Ötən mühazirədəki nümunələri xatırlayırsınız? Serializasiya etmək istədiyimiz Java sinifləri xüsusi interfeysi — java.io.Serializable — reallaşdırmalıdır. Bu, marker interfeys adlanır: o, heç bir metod ehtiva etmir və sadəcə sinfi serializasiya üçün uyğun olaraq “işarələyir”. Sinif bu interfeysi reallaşdırırsa, JVM onun obyektlərini standart vasitələrlə serializə etməyə icazə verir.
Hər şeyi serializasiya etmək məsləhət deyil, çünki bütün obyektləri baytlara çevirmək mümkün və ya lazım deyil. Bəzi obyektlər əməliyyat sisteminin vəziyyətindən, açıq fayllardan və ya şəbəkə bağlantılarından asılıdır. Buna görə Java sinfi açıq şəkildə serializasiya olunan kimi işarələməyi tələb edir.
Marker interfeys — qutunun üzərindəki “qablaşdırmağa icazə verilir” etiketinə bənzəyir. Belə etiket yoxdursa — “qablaşdıran” (JVM) işləməkdən imtina edir.
Nümunə: serializasiya olunan sinfin elan edilməsi
import java.io.Serializable;
public class User implements Serializable {
private String name;
private int age;
// Konstruktor, getter və setterlər
public User(String name, int age) {
this.name = name;
this.age = age;
}
// Gözəllik üçün: toString() metodu
@Override
public String toString() {
return "User{name='" + name + "', age=" + age + "}";
}
}
Nəzərə alın:
- Sadəcə sinifin elanına implements Serializable əlavə etdik.
- Heç bir metodu reallaşdırmağa ehtiyac yoxdur (interfeys boşdur).
- Serializasiya oluna bilən bütün standart Java sinifləri (məsələn, ArrayList, HashMap, String) artıq Serializable-ı reallaşdırır.
2. Öz sinfinizi serializasiya oluna bilən etmək
Qayda № 1: Sadəcə implements Serializable əlavə edin
Sinfin özünə lazım olan budur. Amma nüanslar var!
Vacibdir: istinad olunan bütün obyektlər də serializasiya oluna bilən olmalıdır.
Sinifinizin digər obyektlərə istinad edən sahələri varsa, onlar da serializasiya oluna bilən olmalıdır. Məsələn:
public class Profile implements Serializable {
private User user; // User serializasiya edilə bilən olmalıdır!
private int level;
}
Sahələrdən ən azı biri serializasiya oluna bilmirsə, serializasiya cəhdi zamanı istisna atılacaq.
3. Serializasiya və deserializasiya nümunəsi
Gəlin obyektin fayla necə serializasiya edilib fayldan necə deserializasiya olunduğuna baxaq. Bunun üçün ObjectOutputStream və ObjectInputStream siniflərindən istifadə olunur.
Nümunə: obyekt User-in fayla serializasiyası
import java.io.*;
public class SerializeDemo {
public static void main(String[] args) {
User user = new User("Alice", 30);
// Obyekti fayla yazırıq
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Obyekt uğurla user.ser faylına serializasiya olundu");
} catch (IOException e) {
System.out.println("Serializasiya xətası: " + e.getMessage());
}
}
}
Burada nə baş verir?
- User obyekti yaradırıq.
- ObjectOutputStream axınını açırıq, o isə "user.ser" faylına yazır.
- writeObject(user) çağırırıq. Bu an JVM obyekti bayt axınına çevirir və fayla saxlayır.
Nümunə: obyektin fayldan deserializasiyası
import java.io.*;
public class DeserializeDemo {
public static void main(String[] args) {
// Obyekti fayldan oxuyuruq
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User user = (User) ois.readObject();
System.out.println("Obyekt uğurla bərpa olundu: " + user);
} catch (IOException | ClassNotFoundException e) {
System.out.println("Deserializasiya xətası: " + e.getMessage());
}
}
}
Burada nə baş verir?
- ObjectInputStream axınını açırıq, o isə "user.ser" faylından oxuyur.
- readObject() çağırırıq. JVM obyekti baytlardan bərpa edir.
- Nəticəni lazımi tipə (User) çevirməyi unutmayın, çünki readObject() Object qaytarır.
- Deserializasiya zamanı ClassNotFoundException yarana bilər, əgər User sinfi tapılmasa.
Hamısı birlikdə: serializasiya və deserializasiya
import java.io.*;
public class SerializationExample {
public static void main(String[] args) {
User user = new User("Bob", 22);
// Serializasiya
try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.ser"))) {
oos.writeObject(user);
System.out.println("Serializasiya başa çatdı!");
} catch (IOException e) {
System.out.println("Serializasiya xətası: " + e.getMessage());
}
// Deserializasiya
try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.ser"))) {
User loaded = (User) ois.readObject();
System.out.println("Deserializasiya başa çatdı! " + loaded);
} catch (IOException | ClassNotFoundException e) {
System.out.println("Deserializasiya xətası: " + e.getMessage());
}
}
}
Nəticə:
Serializasiya başa çatdı!
Deserializasiya başa çatdı! User{name='Bob', age=22}
4. Serializasiya zamanı “kapotun altında” nə baş verir
writeObject çağırdığınızda, JVM əvvəlcə sinfin Serializable interfeysini reallaşdırıb-reallaşdırmadığını yoxlayır. Əgər sinif serializasiya olunan kimi işarələnməyibsə, istisna atılır. Sonra JVM obyektin bütün adi sahələrindən (yəni static və transient olmayanlardan) keçərək onların dəyərlərini bayt axınına yazır. Bu sahələr arasında başqa obyektlər varsa, onlar da rekursiv şəkildə serializasiya olunur, ancaq yalnız onlar da Serializable-ı reallaşdırırsa.
Deserializasiya zamanı obyekt adi konstruktor çağırılmadan yaradılır və onun sahələri saxlanmış dəyərlərlə doldurulur — sanki “konstruktorsuz konstruktor” obyekti bayt axınından canlandırır.
Bəzi sahələr serializasiya olunmur. Statik sahələr (static) obyektə deyil, sinfin özünə məxsusdur, buna görə onların dəyərləri saxlanmır. transient kimi işarələnmiş sahələr də ötürülür — bu, müvəqqəti məlumatlar, keş və ya şifrələr kimi məxfi informasiya üçün əlverişlidir.
Serializasiya prosesinin sxemi
flowchart TB
A[Obyekt User yaddaşda] -- writeObject --> B[ObjectOutputStream]
B -- baytları saxlayır --> C[Fayl user.ser]
C -- readObject --> D[ObjectInputStream]
D -- bərpa edir --> E[Obyekt User yaddaşda]
5. Serializable istifadə edərkən tipik səhvlər
Səhv № 1: Serializasiya olunmayan obyektə istinad edən sahə. Əgər User sinfində, məsələn, Thread və ya Socket tipində sahə peyda olarsa, serializasiya işləməyəcək. Bütün obyektləri serializasiya etmək olmur — bunu yadda saxlayın!
Səhv № 2: Serializasiya olunmayan daxili siniflər. Əgər User sinfi static olmayan daxili sinif saxlayırsa, serializasiya işləməyə bilər. Daha yaxşısı static daxili siniflərdən və ya ayrıca sinif fayllarından istifadə etməkdir.
Səhv № 3: static sahəni serializasiya etməyə cəhd. Statik sahələr serializasiya olunmur — onlar sinfə məxsusdur, obyektə yox. Deserializasiyadan sonra static sahənin dəyəri serializə olunmuş obyektin deyil, sinifdə müəyyən edilmiş dəyər olacaq.
Səhv № 4: Sinif versiyalarının uyğunsuzluğu. Əgər serializasiyadan sonra sinfin strukturunu dəyişsəniz (məsələn, sahə əlavə və ya silsəniz) və sonra köhnə obyekti deserializasiya etməyə çalışsanız, InvalidClassException xətası yarana bilər. Versiyalara nəzarət üçün xüsusi serialVersionUID sahəsindən istifadə olunur — bunun haqqında növbəti mühazirədə daha ətraflı danışacağıq.
GO TO FULL VERSION