构造器的多态行为

来源:互联网 发布:java反序列化漏洞扫描 编辑:程序博客网 时间:2024/05/24 02:21


我们知道,在调用父类构造器时,得先要调用子类构造器。如果父类有方法f(),子类对其重写了,恰好父类构造器中用到了这个f()方法,那么这个f()调用的是父类中的f()还是子类中已经重写的f()呢?


不妨用一个例子试试看,这个例子引用自Thinking in java。

class Glyph {void draw() {System.out.println("Glyph.draw()");}Glyph() {draw();}}class RoundGlyph extends Glyph {private int radius = 1;public RoundGlyph(int r) {radius = r;System.out.println("RoundGlyph.RoundGlyph(),radius=" + radius);}void draw() {System.out.println("RoundGlyph.draw(), radius=" + radius);}}public class PolyConstructors {public static void main(String[] args) {new RoundGlyph(5);}}


输出的结果是:

RoundGlyph.draw(), radius=0
RoundGlyph.RoundGlyph(),radius=5


分析:

1.从结果中可以看到draw()调用的是子类中的draw()。这个可以这样理解:非静态非final方法是动态绑定的,方法调用者对象的实际类型来决定调用哪一个方法,这里是通过new RoundGlyph()(虽然此时还是一个未完全初始化的不完整对象)来最终能调用到draw()这个方法,所以应该是调用RoundGlyph中的draw()方法。

2.radius=0。 ???这是怎么回事???现在来解释这个问题。


之前的关于初始化顺序的文章中强调过,构造器的调用是在成员初始化完成之后的。但是这里,通过构造器执行draw()方法的时候,radius进行初始化了么?答案是没有。所以此时打印出的radius是在未赋值之前的默认值0。


关于初始化顺序再说的详细点儿,int radius = 1,这句话实际上给radius赋过两次值,第一次是默认值0,第二次是1。new RoundGlyph()一执行之后,先在堆中划出一片区域,所有的非静态成员都先初始位成了二进制的0,此时的radius是0。等到慢慢初始化,轮到radius了,才第二次赋值为1。所以会出现上面的结果。


因此,编写构造器有一条有效的准则" 用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法"。在构造器内唯一能够安全调用的是基类中的final方法,因为final方法不能被重写(private方法也属于final方法)。