Java的构造器看起来不复杂,只是一个对象的初始化过程,但是在承继和多态中使用时,却暗藏着一些陷阱。
有一天,我高高兴兴地写了一个类Wind去继承类Instrument。至于Instrument的内部有什么实现,我不知道,也不关心,不过既然看起来像个基类,以后也许能用得到它的方法呢。
于是,我先写了一个最简单的Wind类
class Wind extends Instrument { public Wind() { } void play() { System.out.println("it is Wind dididi" ); } }
先简单似乎什么功能都没有,于是我赶紧new了一个Wind。
public class Main { static Wind wind ; public static void main(String[] args) { wind = new Wind(); } }
Run了一下,蒙了。。。
输出:
it is Wind dididi
我啥也没做,为啥play方法就调用啦?
于是,我把Wind类改成这样:
class Wind extends Instrument { private int temp =1; public Wind() { temp = 5; play(); } void play() { System.out.println("it is Wind dididi" + temp); } }
结果输出:
it is Wind dididi0
it is Wind dididi5
显而易见,由上面两个例子,我们已经可以发现,这一定是父类的问题。于是我打开了父类的代码:
class Instrument { public Instrument() { play(); } void play(){ System.out.println("it is Instrument"); } }
发现在父类的构造方法中,调用了play。但是由于对象是Wind类型,play方法已经被覆盖,父类中的play方法没有用了。并且,细心的朋友可以注意到,it is Wind dididi0。temp的值是为0的。也就是说,虽然在实例化这个对象时,编译器已经发现了这个成员,但不会给它初值,而是先去执行父类的构造方法。在父类的构造方法中,temp的值就是0。那么,有没有比父类优先级更高的初始化呢?
是有的,我进一步修改了Wind:
class Wind extends Instrument { static private int temp =1; public Wind() { temp = 5; play(); } void play() { System.out.println("it is Wind dididi" + temp); } }
这样输出的结果为:
it is Wind dididi1
it is Wind dididi5
由此可见,在获取实例时,编译器的最高优先级是static域,然后是父类成员,父类构造方法,再是子类成员,子类构造方法。
同时,这个例子也提醒我们,在构造方法中,只能调用private方法和final修饰的方法,只有这样才是安全的。否则,我们可以通过子类的覆盖,能修改基类的构造方法,给工程带来混乱。
Done!