CodeGym /Kurslar /JAVA 25 SELF /Binar serializasiya zamanı obyektlərin identikliyi

Binar serializasiya zamanı obyektlərin identikliyi

JAVA 25 SELF
Səviyyə , Dərs
Mövcuddur

1. Giriş

Java-da (və ümumiyyətlə OOP-də) hər obyektin identikliyi var — bu, sadəcə sahələrinin dəyərlərinin toplusu deyil, yaddaşda “o kimdir” məsələsidir. İki obyekt “ekvivalent” ola bilər (məsələn, a.equals(b) true qaytarır), lakin identiklik baxımından fərqli ola bilərlər (a != b). Identiklik — iki dəyişənin yaddaşda eyni obyektə istinad etməsidir (a == b).

Serializasiya üçün bu niyə vacibdir?

Obyekt qrafını (məsələn, ağac, siyahı və ya sadəcə eyni daxili obyektə istinad edən iki obyekt) serializə etdikdə, təkcə sahələrin dəyərlərini deyil, həm də obyektlər arasındakı istinadlar strukturunu qorumaq vacibdir.

Əgər qrafınızda təkrarlanan istinadlar (bir neçə obyektin eyni daxili obyektə istinad etməsi) və ya hətta dövrəvi istinadlar (A → B → A) varsa, deserializasiyadan sonra bu əlaqələr olduğu kimi qalmalıdır.

Nümunə: iki obyektiniz A və B var, hər ikisi eyni C obyektinə istinad edir. Serializasiya və deserializasiyadan sonra belə olmalıdır: a.c == b.c (yaddaşda eyni obyekt). Serializasiya sadəcə “obyektləri kopyalasa”, bərpadan sonra iki fərqli C obyekti alınar — bu isə artıq başqa qrafdır.

2. ObjectOutputStream identiklik problemini necə həll edir

Java-da binar serializasiya üçün iki sinifdən istifadə olunur: ObjectOutputStream (yazma üçün) və ObjectInputStream (oxuma üçün).

ObjectOutputStream — sadəcə “obyekti fayla yazmaq” deyil. O, ağıllıdır:

  • Bir yazma axını (bir sessiya) çərçivəsində artıq serializə olunmuş bütün obyektləri izləyir.
  • Əgər artıq serializə olunmuş obyektə istinadla qarşılaşarsa, onu yenidən yazmır, xüsusi “təkrarlanan istinad” (reference handle) qeyd edir.
  • Deserializasiya zamanı ObjectInputStream strukturu elə bərpa edir ki, təkrarlanan istinadlar yaddaşda eyni obyektə göstərsin.

Bu, hətta dövrəvi istinadlar üçün də işləyir! Yəni A → B → A varsa, serializasiya və deserializasiya nə sonsuz döngüyə düşəcək, nə də “yıxılacaq”: istinadlar düzgün bərpa olunacaq.

Bu necə reallaşdırılıb?

ObjectOutputStream-in daxilində artıq serializə olunmuş obyektlərin saxlandığı xüsusi cədvəl (identity map) istifadə olunur. Yeni obyektlə qarşılaşdıqda, obyekt cədvələ əlavə olunur və tam serializə edilir. Cədvəldə olan obyektlə qarşılaşdıqda isə axına yalnız “təkrarlanan istinad” (handle) yazılır.

3. Nümunə ilə nümayiş

Nümunə 1: Dövrəvi istinadlı qraf (A → B, B → A)

import java.io.*;

class Node implements Serializable {
    String name;
    Node next;

    Node(String name) {
        this.name = name;
    }
}

public class CyclicSerializationDemo {
    public static void main(String[] args) throws Exception {
        Node a = new Node("A");
        Node b = new Node("B");
        a.next = b;
        b.next = a; // Dövr!

        // Serializasiya
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cyclic.dat"))) {
            out.writeObject(a);
        }

        // Deserializasiya
        Node a2;
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("cyclic.dat"))) {
            a2 = (Node) in.readObject();
        }

        System.out.println("a2.name = " + a2.name); // A
        System.out.println("a2.next.name = " + a2.next.name); // B
        System.out.println("a2.next.next == a2: " + (a2.next.next == a2)); // true!
    }
}

Nəticə:

a2.name = A
a2.next.name = B
a2.next.next == a2: true

Şərh:
Dövr qorunub! Deserializasiyadan sonra a2.next.next yenidən a2-yə istinad edir.

Nümunə 2: Təkrarlanan istinadlar (A → C, B → C)

import java.io.*;

class Wrapper implements Serializable {
    String name;
    Object ref;

    Wrapper(String name) { this.name = name; }
}

public class SharedReferenceDemo {
    public static void main(String[] args) throws Exception {
        Wrapper a = new Wrapper("A");
        Wrapper b = new Wrapper("B");
        Wrapper c = new Wrapper("C");

        a.ref = c;
        b.ref = c;

        // Serializasiya
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("shared.dat"))) {
            out.writeObject(new Wrapper[] {a, b});
        }

        // Deserializasiya
        Wrapper[] arr;
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("shared.dat"))) {
            arr = (Wrapper[]) in.readObject();
        }

        Wrapper a2 = arr[0];
        Wrapper b2 = arr[1];

        System.out.println("a2.ref == b2.ref: " + (a2.ref == b2.ref)); // true!
        System.out.println("a2.ref.name = " + ((Wrapper)a2.ref).name); // C
    }
}

Nəticə:

a2.ref == b2.ref: true
a2.ref.name = C

Şərh:
Deserializasiyadan sonra hər iki istinad yenidən eyni obyektə göstərir.

4. writeReplace və readResolve ilə əlaqə

Java-da xüsusi metodlar vasitəsilə (de)serializasiya prosesinə “qarışmaq” olar:

writeReplace() — obyekt serializə edilməzdən əvvəl çağrılır. Orijinalın əvəzinə serializə olunacaq başqa obyekt qaytara bilərsiniz.
readResolve() — obyekt deserializasiya edildikdən sonra çağrılır. Elə bir obyekt qaytara bilərsiniz ki, yeni yaradılmış obyektin əvəzinə ondan istifadə edilsin.

Identiklik üzərində təsir:

  • Əgər writeReplace() və ya readResolve() istifadə olunursa, məhz bu metodların qaytardığı obyekt istinadlar cədvəlinə düşəcək.
  • Bu, identikliyi dəyişə bilər: readResolve() yeni/fərqli obyekt qaytararsa, təkrarlanan istinadlar müxtəlif nümunələrə göstərə bilər.
  • Diqqətli olun: identikliyi həm “sındıra” bilərsiniz, həm də məqsədli şəkildə təmin edə bilərsiniz (klassik nümunə — readResolve() ilə Singleton, burada deserializasiyadan sonra bütün istinadlar eyni singleton-a göstərir).

5. Praktika: identikliyin qorunmasını göstərən kod

import java.io.*;

class Shared implements Serializable {
    String value;
    Shared(String value) { this.value = value; }
}

class Holder implements Serializable {
    String name;
    Shared shared;
    Holder(String name, Shared shared) {
        this.name = name;
        this.shared = shared;
    }
}

public class IdentityDemo {
    public static void main(String[] args) throws Exception {
        Shared c = new Shared("C");
        Holder a = new Holder("A", c);
        Holder b = new Holder("B", c);

        // Serializasiya
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("identity.dat"))) {
            out.writeObject(new Holder[] {a, b});
        }

        // Deserializasiya
        Holder[] arr;
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("identity.dat"))) {
            arr = (Holder[]) in.readObject();
        }

        Holder a2 = arr[0];
        Holder b2 = arr[1];

        System.out.println("a2.shared == b2.shared: " + (a2.shared == b2.shared)); // true!
        System.out.println("a2.shared.value = " + a2.shared.value); // C
    }
}

Nəticə:

a2.shared == b2.shared: true
a2.shared.value = C

Şərh:
c obyektinin identikliyi qorunub: deserializasiyadan sonra hər iki istinad eyni nümunəyə göstərir.

6. Yekunlar və tipik səhvlər

Səhv №1: təkrarlanan istinadları olan obyektləri ayrılıqda serializasiya etmək. Əgər hər obyekti öz ObjectOutputStream nümunənizlə yazırsınızsa, aralarındakı istinadlar saxlanılmayacaq — deserializasiya olunmuş nümunələr fərqli olacaq, orijinalda bu eyni istinad olsa belə.

Səhv №2: writeReplace()readResolve() metodlarından səhv istifadə. Bu metodlar (de)serializasiya zamanı obyekti əvəzləyə bilər və bu da onun identikliyini dəyişdirər. Mexanizmi anlamadan istifadə etsəniz, çıxışda gözlənilməz nümunələr əldə edə bilərsiniz.

Səhv №3: ortaq dəyişən istinadların gözlənilməz təsirləri. Bir neçə obyekt eyni dəyişən (mutable) daxili obyektə (məsələn, siyahı) istinad edirsə, deserializasiyadan sonra da bu belə qalacaq. Bir yerdə edilən dəyişiklik hamısına təsir edəcək.

Səhv №4: serializasiyadan sonra “yeni” obyektlər gözləmək. Serializasiya “təzə” struktur yaratmır — o, orijinaldakı kimi ilkin istinadları bərpa edir. Bu, keşlərlə və ya şablon obyektlərlə işləyərkən gözlənilməz ola bilər.

1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Paylaşılan sənədlə birgə işləmə: paylaşılan obyektlərin saxlanması 📝
Paylaşılan sənədlə birgə işləmə: paylaşılan obyektlərin saxlanması 📝
1
Tapşırıq
JAVA 25 SELF, səviyyə, dərs
Bağlanıb
Anbar idarəsi: ayrı saxlanmada obyektlərin eyniliyi 📦
Anbar idarəsi: ayrı saxlanmada obyektlərin eyniliyi 📦
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION