1. Kolleksiyaların daxilində kolleksiyaların serializasiyası
Java-da kolleksiyalar təkcə sadə tipləri (məsələn, String) deyil, digər kolleksiyaları və ya obyektləri də saxlaya bilər. Bu, mürəkkəb strukturlar yaratmağa imkan verir: məsələn, Map<String, List<User>>, burada User — sizin öz sinfinizdir.
Nümunə: Daxili List olan Map-in serializasiyası
Hər istifadəçinin dost siyahısı olan kiçik bir sosial şəbəkə nümunəsinə baxaq.
import java.io.*;
import java.util.*;
class User implements Serializable {
private static final long serialVersionUID = 1L;
String name;
User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" + "name='" + name + '\'' + '}';
}
}
public class SocialNetwork implements Serializable {
private static final long serialVersionUID = 1L;
Map<String, List<User>> friends = new HashMap<>();
public static void main(String[] args) throws IOException, ClassNotFoundException {
SocialNetwork network = new SocialNetwork();
network.friends.put("alice", Arrays.asList(new User("bob"), new User("carol")));
network.friends.put("bob", Collections.singletonList(new User("alice")));
// Serializasiya
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("network.ser"))) {
out.writeObject(network);
}
// Deserializasiya
SocialNetwork loaded;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("network.ser"))) {
loaded = (SocialNetwork) in.readObject();
}
System.out.println("Bərpa edilmiş şəbəkə: " + loaded.friends);
}
}
Belə demək olar: istər HashMap, istər ArrayList, istərsə də User olsun — istifadə olunan bütün tiplər Serializable interfeysini reallaşdırır. Serializasiya zamanı Java avtomatik olaraq bütün daxili kolleksiyaları və obyektləri də keçərək onları yazır. Buna görə də deserializasiyadan sonra siz bütün daxili siyahılar daxil olmaqla tam bərpa olunmuş strukturu əldə edirsiniz.
Nəticə:
Bərpa edilmiş şəbəkə: {alice=[User{name='bob'}, User{name='carol'}], bob=[User{name='alice'}]}
İstənilən səviyyədə iç-içəlik
İstədiyiniz qədər səviyyədə iç-içəlik yarada bilərsiniz: List<List<User>>, Map<String, Map<Integer, List<User>>> — Java rekursiyadan qorxmur (əlbəttə ki, məntiqli hədlər daxilində).
2. İyerarxik obyektlər: irsiyyətli kolleksiyaların serializasiyası
Bəs kolleksiyalarınız irsiyyət prinsipinə görə qurulmuş obyektlərdən ibarətdirsə? Məsələn, sizdə baza sinfi Animal var, kolleksiyada isə həm Cat, həm də Dog obyekti saxlanılırsa?
Nümunə: Varisləri olan kolleksiyanın serializasiyası
import java.io.*;
import java.util.*;
abstract class Animal implements Serializable {
private static final long serialVersionUID = 1L;
String name;
Animal(String name) {
this.name = name;
}
public abstract String speak();
}
class Cat extends Animal {
private static final long serialVersionUID = 1L;
Cat(String name) {
super(name);
}
@Override
public String speak() {
return "Meow!";
}
}
class Dog extends Animal {
private static final long serialVersionUID = 1L;
Dog(String name) {
super(name);
}
@Override
public String speak() {
return "Woof!";
}
}
public class Zoo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
List<Animal> animals = new ArrayList<>();
animals.add(new Cat("Murka"));
animals.add(new Dog("Sharik"));
// Serializasiya
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("zoo.ser"))) {
out.writeObject(animals);
}
// Deserializasiya
List<Animal> loaded;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("zoo.ser"))) {
loaded = (List<Animal>) in.readObject();
}
for (Animal animal : loaded) {
System.out.println(animal.name + " deyir: " + animal.speak());
}
}
}
Nəticə:
Murka deyir: Meow!
Sharik deyir: Woof!
Vacib məqam: Java təkcə baza sinfinin sahələrini deyil, həm də obyektin real tipi haqqında məlumatı serializasiya edir. Buna görə də deserializasiyadan sonra obyektlər öz «pişik» və ya «it» mahiyyətini saxlayır və siz onların metodlarını təhlükəsiz şəkildə çağıra bilərsiniz.
3. Obyekt qraflarının serializasiyası
İndi isə əsl sehrə keçməyin vaxtıdır — obyekt qraflarının serializasiyasına; burada obyektlər bir-birinə istinad edə bilər, təkcə bir-birinin içində yerləşmək məcburiyyətində deyil. Amma əvvəlcə bu qrafların nə olduğuna baxaq.
Obyekt qrafı nədir?
Obyekt qrafı — obyektlərin sahə-istinadlar vasitəsilə bir-biri ilə əlaqəli ola bildiyi strukturdur. Məsələn, nəsil ağacında hər bir şəxsin valideynlərə, övladlara, qardaş və bacılara istinadları ola bilər.
Analoji: Sosial şəbəkədə dostlar qrupu təsəvvür edin: hər bir istifadəçinin dost siyahısı var, bu dostlar da öz növbəsində istifadəçilərdir və onların da öz dostları var və beləliklə davam edir. Bu, obyekt qrafıdır.
Nümunə: İki tərəfli bağlı siyahının serializasiyası
import java.io.*;
class Node implements Serializable {
private static final long serialVersionUID = 1L;
String value;
Node next;
Node prev;
Node(String value) {
this.value = value;
}
}
public class DoublyLinkedListDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// Iki əlaqəli düyün yaradırıq
Node first = new Node("A");
Node second = new Node("B");
first.next = second;
second.prev = first;
// Serializasiya
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("list.ser"))) {
out.writeObject(first);
}
// Deserializasiya
Node loaded;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("list.ser"))) {
loaded = (Node) in.readObject();
}
System.out.println("Birincinin dəyəri: " + loaded.value); // "A"
System.out.println("Növbəti: " + loaded.next.value); // "B"
System.out.println("Sonrakının əvvəlkisi: " + loaded.next.prev.value); // "A"
}
}
Nəzərə alın ki, yalnız bir istinad (first) serializasiya olunur, lakin rekursiv serializasiya sayəsində Java bütün əlaqəli obyektlər boyunca «gəzəcək». Deserializasiya zamanı istinad strukturu tam bərpa olunacaq: loaded.next.prev == loaded true olacaq! Qrafda sikllər olduqda (məsələn, düyünlər bir-birinə istinad edəndə) Java-nın standart serializasiyası düzgün işləyir və sonsuz dövrə düşmür.
4. Daxili və iyerarxik kolleksiyalar: real siniflə nümunə
Model: Kitab kataloqu
Tutaq ki, bizdə Book sinfi var və bu sinif adi kitab və ya elektron nəşr ola bilər (irsiyyət). Həmçinin Library sinfi var və onda janrların xəritəsi (Map<String, List<Book>>) saxlanılır. Hər janr — kitabların siyahısıdır.
import java.io.*;
import java.util.*;
abstract class Book implements Serializable {
private static final long serialVersionUID = 1L;
String title;
Book(String title) {
this.title = title;
}
}
class PaperBook extends Book {
private static final long serialVersionUID = 1L;
int pages;
PaperBook(String title, int pages) {
super(title);
this.pages = pages;
}
}
class EBook extends Book {
private static final long serialVersionUID = 1L;
String format;
EBook(String title, String format) {
super(title);
this.format = format;
}
}
class Library implements Serializable {
private static final long serialVersionUID = 1L;
Map<String, List<Book>> catalog = new HashMap<>();
}
public class CatalogDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Library library = new Library();
library.catalog.put("Fantastika", Arrays.asList(
new PaperBook("Dyuna", 800),
new EBook("Marsianin", "epub")
));
library.catalog.put("Klassika", Collections.singletonList(
new PaperBook("Voyna i mir", 1200)
));
// Serializasiya
try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("library.ser"))) {
out.writeObject(library);
}
// Deserializasiya
Library loaded;
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("library.ser"))) {
loaded = (Library) in.readObject();
}
for (Map.Entry<String, List<Book>> entry : loaded.catalog.entrySet()) {
System.out.println("Janr: " + entry.getKey());
for (Book book : entry.getValue()) {
System.out.println(" - " + book.title + " (" + book.getClass().getSimpleName() + ")");
}
}
}
}
Nəticə:
Janr: Fantastika
- Dyuna (PaperBook)
- Marsianin (EBook)
Janr: Klassika
- Voyna i mir (PaperBook)
Yekun:
- Daxili kolleksiyaların serializasiyası (Map<String, List<Book>>) «qutudan çıxan kimi» işləyir.
- Obyekt tipləri (PaperBook, EBook) qorunur.
- Deserializasiyadan sonra struktur tam şəkildə bərpa olunur.
5. Obyekt qraflarının serializasiyası: «kapotun altında» nə baş verir?
Siz obyekti serializasiya edəndə, Java onun bütün sahələrini (və sahələrin sahələrini və sair) gəzərək hər bir obyekti cəmi bir dəfə serializasiya edir. Obyekt təkrar rast gəlinərsə (məsələn, siklik istinadda), Java onu yenidən serializasiya etmir, xüsusi istinad yazır.
Vizualizasiya (blok-sxema)
graph TD
A[Obyekt A] -- sahə --> B[Obyekt B]
B -- sahə --> C[Obyekt C]
C -- sahə --> A
Java əvvəlcə A-nı, sonra B-ni, sonra C-ni serializasiya edir və yenidən A-ya rast gələndə «artıq serializasiya edilmiş A obyektinə istinad» yazır. Deserializasiya zamanı struktur bütün əlaqələr qorunmaqla bərpa olunur.
6. Qraf serializasiyasının xüsusiyyətləri
- Sikllər problem deyil: Java-nın standart serializasiyası siklik istinadları dəstəkləyir, sonsuz dövrə düşmür və StackOverflow yaratmır.
- Bütün obyektlər serializasiya oluna bilən olmalıdır: qrafdakı obyektlərdən ən azı biri Serializable reallaşdırmırsa, serializasiya həmin obyekt üzərində uğursuz olacaq.
- Eyni obyektlər təkrarlanmır: eyni obyekt qrafın bir neçə yerində rast gəlinərsə, deserializasiyadan sonra bu, eyni obyekt olacaq (istinada görə).
- Obyekt tipləri qorunur: kolleksiya List<Animal> kimi elan olunsa belə, deserializasiyadan sonra real siniflərin obyektlərini (Cat, Dog və s.) alacaqsınız.
7. Daxili və iyerarxik obyektlərin serializasiyasında tipik səhvlər
Səhv №1: Bütün siniflər serializasiya olunmur.
Çox vaxt kolleksiya və ya daxili obyektin daxilində yerləşən öz siniflərinizdən birində implements Serializable əlavə etməyi unudurlar. Nəticə — NotSerializableException və məyusluq. Yerləşdirmə zəncirini yoxlayın!
Səhv №2: Əl ilə serializasiya zamanı istinadların itirilməsi.
Əgər writeObject/readObject metodlarını özünüz reallaşdırırsınızsa və sahələrdən birini (məsələn, valideynə istinadı və ya daxili kolleksiyanı) serializasiya etməyi unudursunuzsa, deserializasiyadan sonra struktur zədələnəcək. Həmişə bərpanı test edin.
Səhv №3: Lazımlı sahələr üçün transient istifadə edilməsi.
Əgər lazımlı sahəni transient kimi işarələsəniz, o, serializasiya axınına düşməyəcək və bərpadan sonra null və ya ilkin dəyərə malik olacaq. Bu, obyekt qrafının bütövlüyünü poza bilər.
Səhv №4: Serializasiya və deserializasiya arasında siniflərin strukturunun dəyişməsi.
Obyekti serializasiya etdikdən sonra sinfin strukturunu dəyişmisinizsə (məsələn, sahə əlavə etmisinizsə), deserializasiya cəhdi zamanı xətalar və ya məlumat itkisi ola bilər. serialVersionUID-dən istifadə edin və uyğunluğu qoruyun.
Səhv №5: Böyük qrafların serializasiyası.
Mürəkkəb qarşılıqlı əlaqələr çox böyük fayllara və uzun serializasiya/deserializasiyaya səbəb ola bilər. Ölçülərə nəzarət edin və imkan daxilində hissələrə ayırın.
Səhv №6: «Çiy» kolleksiyaların serializasiyası.
Əgər kolleksiyanı generic-parametrsiz elan etmisinizsə (məsələn, sadəcə List), deserializasiyadan sonra tipləri açıq göstərmək lazım gələcək ki, bu da ClassCastException ilə nəticələnə bilər. Generiklərdən istifadə edin və tipləri yoxlayın.
GO TO FULL VERSION