"Olá, amigo!"

"Oi, Rishi!"

"Bem, como foi seu dia?"

"Brilhante! Hoje Bilaabo me falou sobre recursão, e Ellie me falou sobre referências fracas e suaves."

"Ela te contou sobre referências fantasmas?"

"Você está falando sobre PhantomReference? Ela mencionou, mas não explicou em detalhes."

"Ótimo, então espero que você não se importe se eu preencher esta lacuna."

"Claro! Vou ouvi-lo com prazer, Rishi!"

"Ótimo. Então vou começar."

"Referências fantasmas são as referências mais fracas de todas. Elas têm efeito apenas se um objeto não tiver nenhuma referência além de referências fantasmas."

Referência Fantasma - 1

"Um PhantomReference é usado em um procedimento de exclusão de objeto complexo.  Isso pode ser necessário quando um objeto faz algo fora da máquina Java, por exemplo, chama funções de sistema operacional de baixo nível ou grava seu estado em um arquivo ou faz outra coisa muito importante."

"Aqui está um exemplo de como você pode usá-lo:"

Exemplo de criação de referências fantasmas
// 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));
}

"Quero chamar a atenção para a última linha novamente. Não apenas o objeto x é passado para a PhantomReference — uma fila especial de referências fantasmas também."

"Por que precisamos dessa fila?"

"Isso é o que eu vou te dizer agora."

"Quando você destrói um objeto mantido por uma referência fantasma, ele é destruído, mas não é deletado da memória! O que você acha disso?!"

"Então, como isso funciona?"

"Existem algumas nuances aqui, então vou começar com as mais simples."

"Se apenas referências fantasmas a um objeto permanecerem, isso é o que acontecerá com ele:"

" Etapa 1 . Durante a próxima coleta de lixo, o método finalize() será chamado no objeto. Mas, se o método finalize() não tiver sido substituído, esta etapa será ignorada e a etapa 2 será executada imediatamente."

" Etapa 2. Durante a próxima coleta de lixo, o objeto é colocado em uma fila especial de objetos fantasmas. Ele será excluído dessa fila quando o método clear() for chamado no PhantomReference."

"Quem chama isso? O objeto foi deletado, certo?"

"Bem, o objeto realmente morreu em nosso mundo (o mundo Java), mas não desapareceu. Ele permanece como um fantasma — a fila de objetos fantasmas ainda contém uma referência a ele. A mesma ReferenceQueue cuja referência nós tão cuidadosamente passado para o construtor PhantomReference ."

"Então esta ReferenceQueue é como a vida após a morte?"

"Mais como um mundo fantasma."

"E um objeto fantasma só pode ser excluído chamando clear() em sua referência fantasma."

"Veja como continuar o exemplo anterior:"

Exemplo de criação de referências fantasmas
// 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();
}

"Entendo que algo está acontecendo aqui. Quase até entendo exatamente o que está acontecendo."

"Mas como você usa isso na prática?"

"Aqui está um exemplo melhor:"

Exemplo de criação de referências fantasmas
// 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));
}
Este thread monitorará a fila fantasma e removerá objetos dela
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();
Esta é uma classe que herda PhantomReference e possui um método 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!");
 }
}

"Fizemos três coisas aqui."

"Primeiro, criamos a classe PhantomInteger , que herda PhantomReference < Integer >."

"Segundo, esta classe tem um método especial close (). A necessidade de chamar este método é o que deu início a tudo isso.

"Em terceiro lugar, declaramos um thread especial: referenceThread . Ele espera em um loop até que outro objeto apareça na fila fantasma. Assim que isso acontece, o thread remove o objeto da fila fantasma e chama seu método close (). E então o método clear(). E é isso. O fantasma pode seguir em frente para um mundo melhor. Ele não nos incomodará mais no nosso."

"Tão interessante, mas deu tudo certo."

"Na verdade, estamos rastreando uma fila de objetos moribundos e, em seguida, podemos chamar um método especial para cada um deles."

"Mas lembre-se, você não pode chamar o método no próprio objeto.  Você não pode obter uma referência a ele! O método get() do PhantomReference sempre retorna null. "

"Mas nós herdamos PhantomReference!"

"Mesmo dentro de uma subclasse de PhantomReference, o método get() retorna nulo."

"Então, apenas salvei uma referência ao objeto no construtor"

"Ah. Mas tal referência seria uma StrongReference, e o objeto nunca terminaria na fila fantasma!"

"Caramba. Ok, desista. Se é impossível, então é impossível."

"Ok, bom. Espero que você tenha aprendido algo valioso com a lição de hoje."

"Sim, havia tanto material novo. E eu pensei que já sabia tudo. Obrigado pela lição, Rishi."

"De nada. É isso, vá relaxar. Mas não se esqueça - temos outra aula esta noite."