在JAVA中,发生在构造器的多态现象,的确很有趣。在一定程度上,可以反映JAVA继承和多态的一些属性。
首先展示一下入口类,因为这个类是不变的,所以放在开始
public clas* **ample
{
public static void main(String[] args)
{
new Point3();
}
};
接着,构建三个三层继承类
abstract class Point1
{
private int x=1;
abstract void Show();
Point1()
{
Show();
}
};
abstract class Point2 extends Point1
{
private int x=2;
abstract void Show();
Point2()
{
Show();
}
};
class Point3 extends Point2
{
private int x=3;
void Show()
{
System.out.println(x);
}
Point3()
{
Show();
}
};
这时,如果编译执行,将会出现以下结果:
0
0
3
如果我们把类构建变成以下形式,结果还是不变的
abstract class Point1
{
private int x;
abstract void Show();
Point1()
{
x=1;
Show();
}
};
abstract class Point2 extends Point1
{
private int x;
abstract void Show();
Point2()
{
x=2;
Show();
}
};
class Point3 extends Point2
{
private int x;
void Show()
{
System.out.println(x);
}
Point3()
{
x=3;
Show();
}
}
这有些让人纳闷了,到底是怎么回事,明明设定了x的初始值,为什么编译出来却是这样的呢?
实际上,这样的结果也是符合常理的。在分析以上现象的时候,必须考虑JAVA的继承模型和继承对象的构造过程。
在JAVA中,继承对象的模型是这样的
[(基子对象) 继承对象增添部分]
在继承的时候,如果子对象与基对象出现重叠的元素,就会覆盖在其基对象的相应部分。
那么,构造以上继承对象的模型是这样的
{ [ (Point1基子对象) Point2基子对象增添部分] Point3对象增添部分}
在构造继承对象的时候,会首先构造其基子对象。这也就是说,如果要创建一个对象,那么构造器一定会第一个构造其继承树种最底层的类,然后再逐层往上构建。但是,如果基构造函书中出现调用了抽象函数的情况(正如上述例子),编译器就会往上构建类(此时,按照JAVA对基本数据类型的默认值进行初始化上层数据)。
那么在上面例子中,整个类的创建过程是有个小故事的:
1 编译器大摇大摆的进入Point3的构造函数(编译器曰:糟糕,这家伙有基类),接着进入其基类Point2的构造函数(编译器曰:郁闷,这家伙他爸还有基类),然后进入Point1的构造函数(编译器笑道:终于到了……)。
2 编译器在基子对象Point1区域,创建x变量并初始化为1。接着试图调用Show()方法(编译器曰:怎么这破方法是个抽象方法?),然后编译器以缺省的方式构建Point1的继承对象(x第一次被被覆盖了,赋值为0)。
3 编译器在缺省的Point2对象中继续试图调用Show()方法(编译器骂道:你呀的戏弄洒家!居然还是抽象方法!),接着编译期以缺省的方式构建Point2的继承对象Point3(x再一次被覆盖了,赋值依然为0)。
4 编译器再一次试图调用Show()方法(编译器终于笑了:老子找了你半天,终于找到了),在Show调用system类的out内嵌类的println方法,输出x的值0(这是第一个0)。
5 完成了Point1的构造,编译器回到了Point2的构造函数。它为x赋值为2,试图调用Show()方法(笔者注:编译器真笨,看来电脑真的是没脑的),接着灰溜溜的继续以缺省的方式构建Point3(x第四次被覆盖了,赋值依然为0)。随后的调用过程同4(这是第二个0)。
6 完成了Point2的构造,编译器拖着疲惫的身躯回到了Point3的构造函数。它为x赋值为3(编译器大怒,曰:不知道哪个混蛋编这程序,累死我了),调用Show()方法(以同样的方式输出x,不再累赘。这是第三个的3)
百般曲折,类的构建完成了。呵呵,挺有意思的。这让我想到书上的一句话:如果类中已经存在构造函数,那么JAVA编译器将不再为类创建缺省的构造函数。从上面例子中,可以看出这句话似乎有些不妥。我觉得应该改为以下形式:如果类中已经存在构造函数,在常规构建类的情况下,编译器不会调用缺省的构造函数。所谓常规的构建,实际上就是指非向下构建的情况。如果向下构建,编译器不可能使用下层构造函数(如果它跑到下层找构造函数,就会形成一个死锁),因此它只能用缺省的方式进行构建。