— Привіт, Аміго!

— Привіт, Ріша!

— Ну як день минув?

— Чудово! Мені сьогодні Білаабо розповів про рекурсію, а Еллі — про слабкі та м'які посилання.

— А про примарні посилання розповідала?

— Ти про PhantomReference? Згадувала, але не докладно розповідала.

— Відмінно, тоді сподіваюся, ти не будеш проти, якщо я заповню цю прогалину.

— Звичайно, я із задоволенням тебе послухаю, Ріша!

— Чудово. Тоді я почну.

Примарні посилання - це найслабші посилання з усіх. Тільки якщо на об'єкт не залишається жодних посилань взагалі, крім примарних, їхній механізм набуває чинності.

phantom Reference - 1

PhantomReference використовується для складної процедури видалення об'єкта. Це може бути необхідно, коли об'єкт щось робить за межами Java-машини, наприклад викликає низькорівневі функції ОС або пише своє стан у файл або ще щось дуже важливе.

Приклад використання:

Приклад створення примарних посилань
//спеціальна черга для примарних об'єктів
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

//список примарних посилань
ArrayList<PhantomReference<Integer> > list = новий ArrayList<PhantomReference< ;Integer>>();

//створюємо 10 об'єктів і додаємо їх у список через примарні посилання
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
list.add(new PhantomReference<Integer>(x, queue));
}

Ще раз звертаю увагу на останній рядок. У PhantomReference передається не тільки об'єкт x, а й спеціальна черга примарних посилань.

— А навіщо потрібна ця черга?

— Ось зараз і розповім.

При знищенні об'єкта, який утримується примарним посиланням, він знищується, але не видаляється з пам'яті! Ось така загогулина, розумієш.

— А це як?

— Тут досить багато нюансів, тож почну з найпростішого.

Якщо на об'єкт залишаються лише примарні посилання, то ось що на нього чекає:

Крок 1. Під час найближчого складання сміття об'єкт буде викликаний метод finalize(). Але якщо метод finalize() не був перевизначений, цей крок пропускається, а виконається відразу крок 2.

Крок 2. Під час наступного складання сміття, об'єкт буде поміщений у спеціальну чергу примарних об'єктів, з якої буде видалено, коли PhantomReference викличуть метод clear().

— А хто його викличе? Адже об'єкт як би видалений, хіба ні?

— Тут справа в тому, що фактично об'єкт помер у нашому (Java) світі, але не зник, а залишився в ньому примарою – на нього зберігається посилання у черзі примарних об'єктів. Те саме ReferenceQueue, посилання на яке ми так дбайливо передаємо в конструктор PhantomReference.

— Тобто. ця ReferenceQueue — це як би потойбічний світ?

— Скоріше, як світ привидів.

І щоб видалити об'єкт-примару, треба викликати clear() у його примарного посилання.

Ось як можна продовжити попередній приклад:

Приклад створення примарних посилань
//спеціальна черга для примарних об'єктів
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

//список примарних посилань
ArrayList<PhantomReference<Integer> ;> list = новий ArrayList<PhantomReference< ;Integer>>();

//створюємо 10 об'єктів і додаємо їх у список через примарні посилання
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
list.add(new PhantomReference<Integer>(x, queue));
}

//викликаємо збирач сміття, сподіваємося, що він нас послухається :)
//він повинен убити всі «примарно досяжні» об'єкти і помістити їх у чергу
//Привидів
System.gc();

//Дістаємо з черги всі об'єкти
Reference<? extends Integer>referenceFromQueue;
while ((referenceFromQueue = queue.poll()) != null)
{
 //виводимо об'єкт на екран
 System.out.println(referenceFromQueue.get());
 //очищаємо посилання
 referenceFromQueue.clear();
}

— Щось тут відбувається – це зрозуміло. Навіть майже зрозуміло, що саме відбувається.

Але як це використовувати на практиці?

— Ось тобі більш адекватний приклад:

Приклад створення примарних посилань
//спеціальна черга для примарних об'єктів
ReferenceQueue<Integer> queue = new ReferenceQueue<Integer>();

//список примарних посилань
ArrayList<PhantomInteger> list = new ArrayList<PhantomInteger>();

//створюємо 10 об'єктів і додаємо їх у список через примарні посилання
for ( int i = 0; i < 10; i++)
{
 Integer x = new Integer(i);
list.add(new PhantomInteger (x, queue));
}
Ця нитка стежитиме за примарною чергою і видалятиме звідти об'єкти
Thread referenceThread = new Thread()
{
 public void run()
 {
  while (true)
  {
   try
   {
    //отримуємо новий об'єкт із черги, якщо об'єкта немає - чекаємо!
   PhantomInteger ref=(PhantomInteger)queue.remove();
    //викликаємо у нього метод close
   ref.close();
    ref.clear();
   }
   catch (Exception ex)
   {
    // пишемо в лог помилки
   }
  }
 }
};
//запускаємо потік у службовому режимі.
referenceThread.setDaemon(true);
referenceThread.start();
Це клас, успадкований від PhantomReference, має метод 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 повністю відхилений!");
 }
}

Ми тут зробили три речі.

По-перше, ми створили клас PhantomInteger, який успадкували від PhantomReference<Integer>.

По-друге, цей клас має спеціальний метод - close(), заради виклику якого як би все це й починається.

По-третє, ми оголосили спеціальну нитку — referenceThread. Вона у циклі чекає, поки в черзі примар не з'явиться ще один об'єкт. Як тільки він з'являється, вона видаляє його з черги привидів, а потім викликає в нього метод close (span). Потім метод clear(). І все – привид може переходити до наступного найкращого світу. У нашому він нас більше не потурбує.

— Як цікаво, проте все вийшло.

— Ми фактично відстежуємо чергу вмираючих об'єктів, і потім для кожного можемо викликати спеціальний метод.

Але, зваж, ти не можеш викликати метод самого об'єкта. Посилання на нього отримати не можна! Метод get() у PhantomReference завжди повертає null.

— Але ж ми успадковуємося від PhantomReference!

— Навіть усередині спадкоємця PhantomReference, метод get() повертає null.

— Тоді я просто збережу посилання на об'єкт у конструкторі

— Ага. Але тоді це посилання буде StrongReference, і об'єкт ніколи не потрапить у чергу примар!

— Млинець. Гаразд, здаюся. Не можна так не можна.

— От і відмінно. Сподіваюся, ти винесеш для себе щось цінне із сьогоднішнього уроку.

— Так стільки нового матеріалу. А я гадав, що вже все знаю. Дякую тобі за урок, Ріша.

— Будь ласка. Все, йди відпочивай. Але не забудь, у нас увечері ще урок.