class Base
{
private int val;
Base()
{
val = lookup();
}
public int lookup()
{
int num = dbLookup();//假設此時num=5
return 5;
}
public int value()
{
return val;
}
}
class Base的構造函數調用了一個non-final函數lookup(),查詢數據庫中的一些數據。這段代碼的運轉與設想相同,Base的instance數據val被賦值爲5.
想像一下,如果一個derived class覆寫了Base的lookup()會怎麼樣?這種做法可能導致不易察覺的結果。例如:
class Base
{
private int val;
Base()
{
val = lookup();
}
public int lookup()
{
int num = dbLookup();
return 5;
}
public int value()
{
return val;
}
}
class Derived extends Base
{
private int num = 10;
public int lookup()
{
return num;
}
}
class Test
{
public static void main(String args[])
{
Derived d = new Derived();
System.out.println("From main() d.value() returns " +
d.value());
}
}
代碼輸出如下:
From main() d.value() returns 0
問題出在Derived的lookup()返回值居然是0。你可能奇怪這是怎麼發生的,明明已經有了函數實現代碼呀!該函數返回instance變量num,它在instance變量初始化時被賦值爲10。事實真相是,當Derived的lookup()開始執行時,其instance變量初始化工作還未來得及進行呢。
注意,這個lookup()是在Derived對象建構期間由Base構造函數調用的,而Base構造函數是被Derived構造函數自動調用的。當執行權進入Deived的lookup()時,其instance變量初始化行爲尚未進行。這種情況下,那些instance變量僅僅被設置爲缺省初值(default initial values)。因此當時的val被設置爲0,於是0被傳出去(Praxis32非常細緻地分析了對象的建構和初始化步驟)。
當構造函數調用non-final函數時,就有可能發生這種錯誤。如果該函數被一個derived class覆寫,而且該函數返回一個在“instance變量初始化期間”被初始化的值,就會產生問題。這種錯誤或許不太常見。不過知道它的存在還是好的,萬一哪天遇上了,就可以節省大量時間。