"Cześć, Amigo!"

"Cześć, Rysiu!"

– No, jak ci minął dzień?

„Wspaniale! Dzisiaj Bilaabo opowiedział mi o rekurencji, a Ellie o słabych i miękkich referencjach”.

"Opowiedziała ci o odniesieniach do fantomów?"

„Mówisz o PhantomReference? Wspomniała o tym, ale nie wyjaśniła szczegółowo”.

- Świetnie, mam nadzieję, że nie będziesz miał nic przeciwko, jeśli wypełnię tę lukę.

"Oczywiście! Z przyjemnością cię wysłucham, Rishi!"

„Świetnie. W takim razie zacznę”.

„Widmowe referencje są najsłabszymi ze wszystkich referencji. Działają tylko wtedy, gdy obiekt nie ma żadnych innych referencji niż fantomowe referencje”.

PhantomReference - 1

„PhantomReference jest używany w złożonej procedurze usuwania obiektów.  Może to być konieczne, gdy obiekt wykonuje coś poza maszyną Java, np. wywołuje niskopoziomowe funkcje systemu operacyjnego, zapisuje swój stan do pliku lub wykonuje coś innego, bardzo ważnego”.

„Oto przykład, jak możesz go użyć:”

Przykład tworzenia fantomowych odniesień
// Special queue for phantom objects
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

// List of phantom references
ArrayList<PhantomReference<Integer>> list = new ArrayList<PhantomReference<Integer>>();

// Create 10 objects and add them to the list using phantom references
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
 list.add(new PhantomReference<Integer>(x, queue));
}

„Chcę jeszcze raz zwrócić uwagę na ostatnią linię. Nie tylko obiekt x jest przekazywany do PhantomReference jest również przekazywana specjalna kolejka odwołań fantomowych”.

„Po co nam ta kolejka?”

– Właśnie to ci teraz powiem.

„Kiedy zniszczysz obiekt przetrzymywany przez fantomowe odniesienie, zostanie on zniszczony, ale nie zostanie usunięty z pamięci! Co o tym myślisz?!”

— Więc jak to działa?

„Jest tu sporo niuansów, więc zacznę od najprostszego”.

„Jeśli pozostaną tylko fantomowe odniesienia do obiektu, oto co się z nim stanie:”

Krok 1. Podczas następnego wyrzucania elementów bezużytecznych metoda finalize() zostanie wywołana na obiekcie. Ale jeśli metoda finalize() nie została zastąpiona, ten krok jest pomijany, a krok 2 jest wykonywany natychmiast”.

Krok 2. Podczas następnego wyrzucania elementów bezużytecznych obiekt jest umieszczany w specjalnej kolejce obiektów fantomowych. Zostanie usunięty z tej kolejki, gdy metoda clear() zostanie wywołana na PhantomReference.”

„Kto to nazywa? Obiekt został usunięty, prawda?”

„Cóż, obiekt rzeczywiście umarł w naszym świecie (świecie Javy), ale nie zniknął. Pozostaje jako widmo — kolejka obiektów widmowych wciąż zawiera do niego odniesienie. Ta sama kolejka referencyjna, której odniesienie tak dokładnie przekazywane do konstruktora PhantomReference ."

„Więc ta kolejka referencyjna jest jak życie pozagrobowe?”

„Bardziej jak fantomowy świat”.

„Obiekt fantomowy można usunąć tylko przez wywołanie funkcji clear() na jego odwołaniu fantomowym”.

„Oto jak kontynuować poprzedni przykład:”

Przykład tworzenia fantomowych odniesień
// Special queue for phantom objects
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

// List of phantom references
ArrayList<PhantomReference<Integer>> list = new ArrayList<PhantomReference<Integer>>();

// Create 10 objects and add them to the list using phantom references
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
 list.add(new PhantomReference<Integer>(x, queue));
}

// Call the garbage collector and hope it will listen to us :)
// It should destroy all phantom reachable objects and put them in the queue
// of phantoms
System.gc();

// Get all objects from the queue
Reference<? extends Integer>referenceFromQueue;
while ((referenceFromQueue = queue.poll()) != null)
{
 // Display the object on the screen
 System.out.println(referenceFromQueue.get());

 // Clear the reference
 referenceFromQueue.clear();
}

„Rozumiem, że coś się tu dzieje. Prawie nawet dokładnie rozumiem, co się dzieje”.

„Ale jak to wykorzystać w praktyce?”

„Oto lepszy przykład:”

Przykład tworzenia fantomowych odniesień
// Special queue for phantom objects
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

// List of phantom references
ArrayList<PhantomInteger> list = new ArrayList<PhantomInteger>();

// Create 10 objects and add them to the list using phantom references
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
 list.add(new PhantomInteger (x, queue));
}
Ten wątek będzie monitorował kolejkę widmową i usuwał z niej obiekty
Thread referenceThread = new Thread()
{
 public void run()
 {
  while (true)
  {
   try
   {
    // Get the new object from the queue. If there is no object, then we wait!
    PhantomInteger ref = (PhantomInteger)queue.remove();
    // Call the close method on it
    ref.close();
    ref.clear();
   }
   catch (Exception ex)
   {
    // Write errors to a log
   }
  }
 }
};
// Run the thread as a daemon
referenceThread.setDaemon(true);
referenceThread.start();
Jest to klasa, która dziedziczy PhantomReference i ma metodę close().
static class PhantomInteger extends PhantomReference<Integer>
{
 PhantomInteger(Integer referent, ReferenceQueue<? super Integer> queue)
 {
  super(referent, queue);
 }

 private void close()
 {
  System.out.println("Bad Integer totally destroyed!");
 }
}

„Zrobiliśmy tutaj trzy rzeczy”.

„Najpierw stworzyliśmy klasę PhantomInteger , która dziedziczy PhantomReference < Integer >”.

„Po drugie, ta klasa ma specjalną metodę close (). Konieczność wywołania tej metody jest tym, od czego to wszystko się zaczęło.

„Po trzecie, zadeklarowaliśmy specjalny wątek: referenceThread . Oczekuje on w pętli, aż w kolejce fantomu pojawi się kolejny obiekt. Gdy tylko to się stanie, wątek usuwa obiekt z kolejki fantomu i wywołuje swoją metodę close (). metoda clear(). I to wszystko. Upiór może przenieść się do lepszego świata. Nie będzie już nam przeszkadzał w naszym."

„Tak interesujące, ale wszystko się udało”.

„Właściwie śledzimy kolejkę umierających obiektów, a następnie możemy wywołać specjalną metodę dla każdego z nich”.

„Ale pamiętaj, nie możesz wywołać metody na samym obiekcie.  Nie możesz uzyskać do niego odwołania! Metoda get() PhantomReference zawsze zwraca wartość null ” .

„Ale my dziedziczymy PhantomReference!”

„Nawet wewnątrz podklasy PhantomReference metoda get() zwraca wartość null”.

„Więc po prostu zapisuję odwołanie do obiektu w konstruktorze”

„Ach. Ale takie odniesienie byłoby Silnym Odniesieniem, a obiekt nigdy nie znalazłby się w kolejce fantomów!”

„Cholera. Dobra, poddaj się. Jeśli to jest niemożliwe, to jest niemożliwe”.

- Okej, dobrze. Mam nadzieję, że nauczyłeś się czegoś wartościowego z dzisiejszej lekcji.

"Tak, było tak dużo nowego materiału. A myślałem, że już wszystko wiem. Dziękuję za lekcję, Rishi."

„Nie ma za co. To tyle, idź się zrelaksować. Ale nie zapomnij — mamy kolejną lekcję tego wieczoru”.