在構造函數內調用non-final函數時要當心

當程序新建一個class對象時,class構造函數會被調用。構造函數的目的在於將對象初始化。構造函數在運行期間可以調用class的某些函數,這很普遍,因爲那些被調用的函數或許包含一些初始化動作。舉個例子:

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變量初始化期間”被初始化的值,就會產生問題。這種錯誤或許不太常見。不過知道它的存在還是好的,萬一哪天遇上了,就可以節省大量時間。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章