Java謎題6:雞與蛋-解決方案

jdon發表於2019-09-23

如果沒有給出一個(non-null)Chicken,則Eggthrow 的建構函式丟擲 NullPointerException ,反之亦然。那麼您如何獲得對它們中任何一個的引用?
使這成為可能(甚至是偶然發生的)的一個常見缺陷是呼叫一個可以在建構函式的子類中重寫的方法。但事實並非如此。您可以重寫一種方法,使建構函式不呼叫,但是Java最終呼叫:finalize()。該方法在物件被垃圾回收之前被呼叫,以使其有機會清理其資源。即使建構函式丟擲異常,它也會被呼叫,因此透過重寫它,我們可以獲得對未完全建立的egg的引用。

package creator;
 
import chicken.Chicken;
import chicken.Egg;
 
public class Creator {
    static class FirstEgg extends Egg {
        FirstEgg() {
            super(null);
        }
 
        @Override
        protected void finalize() {
            new Chicken(this).ask();
        }
    }
 
    public static void main(String[] args) throws Exception {
        try {
            new FirstEgg();
        } catch (NullPointerException e) {
        }
         
        // there are ways to force garbage collection harder,
        // but this works good enough for me 
        System.gc();
        System.runFinalization();
        Thread.sleep(1000);
    }
}


眾所周知,終結機制有幾個 問題,還有更好的選擇。在Java的安全編碼標準中,有多種方法可以抵禦這種情況。另一種方法是在從Object類中呼叫建構函式之前引發異常。這意味著在呼叫super()之前執行此工作(或使用this()呼叫另一個建構函式)。從Java 6開始,這保證finalize()不會被呼叫。例如,在呼叫 this(Object)之前,我們先取消對mom.first的引用,以確保在沒有媽媽的情況下無法建立雞蛋:

public class Egg {
    final Object first;
     
    public Egg(Chicken mom) {
        this(mom.first);
    }
     
    private Egg(Object first) {
        this.first = first;
    }
}


 

相關文章