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!

posted on 2015-09-07 23:54  Fishbonell  阅读(154)  评论(0编辑  收藏  举报