"Ciao, Amico!"

"Ciao Rishi!"

"Bene, com'è andata la tua giornata?"

"Fantastico! Oggi Bilaabo mi ha parlato della ricorsione, ed Ellie mi ha parlato di riferimenti deboli e morbidi."

"Ti ha parlato di riferimenti fantasma?"

"Stai parlando di PhantomReference? Ne ha parlato, ma non l'ha spiegato in dettaglio."

"Fantastico, allora spero che non ti dispiaccia se colmo questa lacuna."

"Certo! Ti ascolterò con piacere, Rishi!"

"Fantastico. Allora comincio io."

"I riferimenti fantasma sono i riferimenti più deboli di tutti. Hanno effetto solo se un oggetto non ha alcun riferimento diverso dai riferimenti fantasma."

Riferimento fantasma - 1

"Un PhantomReference viene utilizzato in una complessa procedura di eliminazione di oggetti.  Ciò può essere necessario quando un oggetto fa qualcosa al di fuori della macchina Java, ad esempio chiama funzioni del sistema operativo di basso livello o scrive il suo stato in un file o fa qualcos'altro di molto importante."

"Ecco un esempio di come potresti usarlo:"

Esempio di creazione di riferimenti fantasma
// 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));
}

"Voglio attirare nuovamente l'attenzione sull'ultima riga. Non solo l'oggetto x viene passato al PhantomReference , ma anche una coda speciale di riferimenti fantasma."

"Perché abbiamo bisogno di questa coda?"

"Questo è quello che ti dirò ora."

"Quando distruggi un oggetto tenuto da un riferimento fantasma, viene distrutto, ma non viene cancellato dalla memoria! Cosa ne pensi?!"

"Allora, come funziona?"

"Ci sono alcune sfumature qui, quindi inizierò con il più semplice."

"Se rimangono solo riferimenti fantasma a un oggetto, allora questo è ciò che gli accadrà:"

" Passaggio 1 . Durante la successiva Garbage Collection, il metodo finalize() verrà chiamato sull'oggetto. Ma, se il metodo finalize() non è stato sovrascritto, questo passaggio viene saltato e il passaggio 2 viene eseguito immediatamente."

" Passaggio 2 . Durante la successiva raccolta dei rifiuti, l'oggetto viene inserito in una coda speciale di oggetti fantasma. Verrà eliminato da questa coda quando il metodo clear() viene chiamato su PhantomReference."

"Chi lo chiama? L'oggetto è stato cancellato, vero?"

"Bene, l'oggetto è davvero morto nel nostro mondo (il mondo Java), ma non è scomparso. Rimane come un fantasma: la coda di oggetti fantasma contiene ancora un riferimento ad esso. La stessa ReferenceQueue di cui abbiamo fatto riferimento con tanta attenzione passato al costruttore PhantomReference ."

"Quindi questa ReferenceQueue è come l'aldilà?"

"Più come un mondo fantasma."

"E un oggetto fantasma può essere eliminato solo chiamando clear() sul suo riferimento fantasma."

"Ecco come continuare l'esempio precedente:"

Esempio di creazione di riferimenti fantasma
// 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();
}

"Capisco che sta succedendo qualcosa qui. Capisco quasi anche esattamente cosa sta succedendo."

"Ma come lo usi in pratica?"

"Ecco un esempio migliore:"

Esempio di creazione di riferimenti fantasma
// 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));
}
Questo thread monitorerà la coda fantasma e rimuoverà gli oggetti da essa
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();
Questa è una classe che eredita PhantomReference e ha un metodo 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!");
 }
}

"Abbiamo fatto tre cose qui."

"Per prima cosa, abbiamo creato la classe PhantomInteger , che eredita PhantomReference < Integer >."

"In secondo luogo, questa classe ha uno speciale metodo close (). La necessità di chiamare questo metodo è ciò che ha dato inizio a tutto questo.

"In terzo luogo, abbiamo dichiarato un thread speciale: referenceThread . Attende in un ciclo finché un altro oggetto non appare nella coda fantasma. Non appena ciò accade, il thread rimuove l'oggetto dalla coda fantasma e chiama il suo metodo close (). E poi il metodo clear(). E questo è tutto. Il fantasma può passare a un mondo migliore. Non ci disturberà più nel nostro."

"Così interessante, ma ha funzionato tutto."

"In realtà stiamo monitorando una coda di oggetti morenti, e quindi possiamo chiamare un metodo speciale per ciascuno di essi."

"Ma ricorda, non puoi chiamare il metodo sull'oggetto stesso.  Non puoi ottenere un riferimento ad esso! Il metodo get() di PhantomReference restituisce sempre null. "

"Ma ereditiamo PhantomReference!"

"Anche all'interno di una sottoclasse di PhantomReference, il metodo get() restituisce null."

"Quindi ho appena salvato un riferimento all'oggetto nel costruttore"

"Ah. Ma una referenza del genere sarebbe una StrongReference, e l'oggetto non finirà mai nella coda fantasma!"

"Dang. Okay, arrenditi. Se è impossibile, allora è impossibile."

"Okay, bene. Spero che tu abbia imparato qualcosa di prezioso dalla lezione di oggi."

"Sì, c'era così tanto materiale nuovo. E pensavo di sapere già tutto. Grazie per la lezione, Rishi."

"Prego. Ecco fatto, rilassati. Ma non dimenticare: stasera abbiamo un'altra lezione."