“嗨,阿米戈!”
“嗨,里希!”
“嗯,今天過得怎麼樣?”
“太棒了!今天 Bilaabo 告訴我遞歸,Ellie 告訴我弱引用和軟引用。”
“她有沒有告訴你有關幻影參考的事?”
“你是說PhantomReference嗎?她提到了,但沒有詳細解釋。”
“好,那我來填補這個空缺,希望你不要介意。”
“當然!我會很樂意聽你的,Rishi!”
“太好了,那我開始了。”
“幻影引用是所有引用中最弱的。只有當一個對象除了幻影引用之外根本沒有任何引用時它們才有效。”

“PhantomReference 用於復雜的對象刪除過程。 當對像在 Java 機器之外做某事時,這可能是必要的,例如,它調用低級操作系統函數或將其狀態寫入文件或做其他非常重要的事情。”
“這是您可能如何使用它的示例:”
// 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));
}
“我想再次提請注意最後一行。對象 x 不僅傳遞給了 PhantomReference——還有一個特殊的幻象引用隊列。”
“為什麼我們需要這個隊列?”
“這就是我現在要告訴你的。”
“當你銷毀一個由幻影引用持有的對象時,它會被銷毀,但它並沒有從內存中刪除!你對此有何看法?!”
“那麼,它是如何工作的?”
“這裡有很多細微差別,所以我將從最簡單的開始。”
“如果只剩下對一個對象的幻影引用,那麼這就是它會發生的事情:”
"第 1 步。在下一次垃圾收集期間,將在對像上調用 finalize() 方法。但是,如果 finalize() 方法尚未被覆蓋,則跳過此步驟,並立即執行第 2 步。"
“第 2 步。在下一次垃圾收集期間,該對像被放置在一個特殊的幻像對象隊列中。當對 PhantomReference 調用 clear() 方法時,它將從此隊列中刪除。”
“誰叫的?對像被刪除了吧?”
“好吧,這個對象確實在我們的世界(Java 世界)中消亡了,但它並沒有消失。它仍然是一個幻影——幻影對象的隊列仍然持有對它的引用。我們非常仔細地引用了同一個ReferenceQueue傳遞給PhantomReference構造函數。”
“所以這個 ReferenceQueue 就像來世?”
“更像是一個幻影世界。”
“而一個幻影對像只能通過在它的幻影引用上調用 clear() 來刪除。”
“這裡是如何繼續前面的例子:”
// 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();
}
“我知道這裡正在發生一些事情。我什至幾乎完全理解正在發生的事情。”
“但是你如何在實踐中使用它呢?”
“這是一個更好的例子:”
// 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));
}
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();
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!");
}
}
“我們在這裡做了三件事。”
“首先,我們創建了PhantomInteger類,它繼承了PhantomReference <Integer> 。”
“其次,這個類有一個特殊的close () 方法。調用這個方法的需要是這一切開始的原因。
”第三,我們聲明了一個特殊的線程:referenceThread。它在一個循環中等待,直到另一個對像出現在幻影隊列中。一旦發生這種情況,線程就會從幻影隊列中移除該對象並調用它的close()方法。然後clear() 方法。僅此而已。幻影可以移動到一個更美好的世界。它不會再困擾我們。”
“太有趣了,但一切都成功了。”
“我們實際上是在跟踪一列垂死的對象,然後我們可以為每個對象調用一個特殊的方法。”
“但請記住,您不能調用對象本身的方法。 您無法獲得對它的引用!PhantomReference 的 get() 方法始終返回 null。 ”
“但是我們繼承了 PhantomReference!”
“即使在 PhantomReference 的子類中,get() 方法也會返回 null。”
“所以我只是在構造函數中保存對對象的引用”
“啊。但這樣的引用將是一個強引用,並且該對象永遠不會在幻影隊列中結束!”
“靠,行了,放棄吧,不行那就不行。”
“好的,很好。我希望你從今天的課程中學到了一些有價值的東西。”
“是的,有這麼多新材料。我以為我已經知道了一切。謝謝你的課,Rishi。”
“不客氣。就這樣吧,去放鬆吧。但別忘了——我們今晚還有一節課。”
GO TO FULL VERSION