在构造函数内调用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变量初始化期间”被初始化的值,就会产生问题。这种错误或许不太常见。不过知道它的存在还是好的,万一哪天遇上了,就可以节省大量时间。

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