这里想问一个java函数调用顺序的问题public class Base {
public Base(){
this.sayHello();
System.out.println("Base");
}

public void sayHello(){
System.out.println("sayHello in base");
}
}public class Derived extends Base {
public Derived (){
this.sayHello();
System.out.println("Derived");
}

public void sayHello(){
System.out.println("sayHello in Derived");
}
}public class Test { /**
 * @param args
 */
public static void main(String[] args) {
Derived d = new Derived();
}}
这里生成一个子类的对象,生成子类对象的时候,在子类执行构造函数时调用父类的构造函数。
父类构造函数中有this.sayHello();方法。这里执行的是子类的sayHello方法。
问题就在这里,为什么是执行子类的sayHello方法呢?如果是c++的话执行的是父类的sayHello方法。
因为java里面的函数都是virtual的,所以sayHello方法在子类里被复写了,所以执行了子类的sayHello方法。
但是在执行父类的构造函数的时候,子类应该还没有生成啊,他怎么能执行子类的sayHello方法呢?
如果说子类已经生成了,为什么没有执行子类的构造函数呢?
非常不解,望高手指点!

解决方案 »

  1.   

    应该是你的子类里的sayhello方法将父类的同名方法覆盖了,然后他调用的就是你子类中的sayhello方法了。
      

  2.   

    这里生成一个子类的对象,生成子类对象的时候,在子类执行构造函数时调用父类的构造函数。 
    父类构造函数中有this.sayHello();方法。这里执行的是子类的sayHello方法。 
    问题就在这里,为什么是执行子类的sayHello方法呢?
      //因为 : Derived d = new Derived(); 这里new出来的是Derived的对象,所以执行的是子类的sayHello方法但是在执行父类的构造函数的时候,子类应该还没有生成啊,他怎么能执行子类的sayHello方法呢? 这里是先调用父类的构造函数,再调用子类的构造函数,建议你用Debug调试一下。
      

  3.   

    这就是构造函数里的多态情况, Think in java里有详述
    建议基类里要调用的方法不要写成公有的或保护的, 以免出现这种情况
      

  4.   

    应该是你的子类里的sayhello方法将父类的同名方法覆盖了,然后他调用的就是你子类中的sayhello方法了。
    当你new Derived()的时候,先调用父类,因为子类override了父类的方法,所以得到如下结果:
    sayHello in Derived
    Base
    sayHello in Derived
    Derived
      

  5.   


    感觉大家还是没有解释到问题的关键。//因为 : Derived d = new Derived(); 这里new出来的是Derived的对象,所以执行的是子类的sayHello方法 
    但是sayHello方法是在父类的构造函数里调用的,子类都没有生成,怎么调用子类的方法了?这里是先调用父类的构造函数,再调用子类的构造函数,建议你用Debug调试一下。
    对啊,就是因为是这样的顺序,所以在父类执行的时候怎么可能子类生成了呢?子类都没生成,怎么能调用子类的方法呢?
      

  6.   

    多态的问题阿..都不愿意调试阿?  为了更详细的说出调用顺序我刚才特地去调试了下.public class Test {
    public static void main(String[] args) {
    Sub s=new Sub();
    }
    }class Base
    {
    public Base()
    {
    this.sayHello();
    System.out.println("Base");
    }

    public void sayHello()
    {
    System.out.println("sayHello in Base");
    }
    }class Sub extends Base
    {
    public Sub()
    {
    this.sayHello();
    System.out.println("Sub");
    }

    public void sayHello()
    {
    System.out.println("sayHello in Sub");
    }
    }当你创建Sub实例的时候,执行Sub类的构造方法,因为这里没有显式的调用Sub类的构造方法,所以编译的时候会自动调用父类的无参构造方法,所以在执行this.sayHello();之前会先调用Base();可能疑惑的在这个地方,Base类里面是this.sayHello()阿,也就是调用本类的,那为什么会调用到子类上面去的.? 这是因为子类重写了父类的方法, this只是个隐式参数而已,代表的是当前对象.  然而你看你main方法里面当前对象是什么呢?  是new Sub();所以这里的this代表的是new Sub(),相当与是Base b=new Sub(); 因为重写所发生的多态,所以运行时会自动的去找Sub类的sayHello()方法.!  子类里面的方法就不用再多说了吧?  关键是this这个关键字的理解.  记住this只是代表当前对象而已..
      

  7.   

    可能疑惑的在这个地方,Base类里面是this.sayHello()阿,也就是调用本类的,那为什么会调用到子类上面去的.? 这是因为子类重写了父类的方法, this只是个隐式参数而已,代表的是当前对象.  然而你看你main方法里面当前对象是什么呢?  是new Sub();所以这里的this代表的是new Sub(),相当与是Base b=new Sub(); 因为重写所发生的多态,所以运行时会自动的去找Sub类的sayHello()方法.!  对,子类是new sub(),但是就像你说的,在base类的构造函数里执行sayhello,这时候连base对象都没有生成完成,哪来的sub对象呢?没有sub对象,又如何执行子类的sayhello方法呢?如果这时候已经有sub对象了,那为何没有执行sub的构造函数呢?我感觉大家还是没有讲到关键地方。
      

  8.   

    大家为什么都不能理解我的意思呢?
    debug有什么用呢?
    debug的时候在父类的构造函数执行sayHello方法的时候进入到了子类的sayHello方法。
    大家一般都会说是因为子类复写了父类的sayHello方法。
    但是这时候子类都没有生成,又怎么能执行子类的方法呢?如果这时候子类已经有了,为什么没有执行子类的构造函数呢?
    请大家认真阅读,别再误解,老说调试下就知道了,这不是调试的问题。
      

  9.   

    这样吧.这里关键是this关键字对吧?我这样来说一下..this关键字是代表当前对象的引用,看清楚,是引用.而由于存在重写,所以关键是这个引用具体指向什么对象的问题了对吧?
    下面我把你的程序改一下.我再来说, 看注释public class Test {
        public static void main(String[] args) {
            Sub s=new Sub();
            s.sayHello();
        }
    }class Base
    {
        public Base()
        {
            //this.sayHello();
            //System.out.println("Base");
        }
        
        public void sayHello()
        {
            System.out.println("sayHello in Base");
        }
        
        //请看这个方法f(), 方法f调用当前对象的sayHello()对吧?而此时,因为有子类重写了父类,所以在编译期是无法得知这个sayHello()到底是哪个版本吧? 只有到了运行期,Sub s=new Sub(); 因为继承,所以s继承了方法f();那么这个this是当前对象的引用,也就是父类的引用,指向子类对象的实例 .. 所以在运行期  这个this就相当于是Base b=s;所以很自然的,这个运行的结果是子类版本的sayHello();
        public void f()
        {
            this.sayHello();
        }
    }class Sub extends Base
    {    
        public Sub()
        {
            //this.sayHello();
            //System.out.println("Sub");
        }
        
        public void sayHello()
        {
            System.out.println("sayHello in Sub");
        }
    }
    不知道各位明白没?  还是多态..  好好想下吧
      

  10.   

    忘记打换行了  代码在上面
    请看这个方法f(), 方法f调用当前对象的sayHello()对吧?而此时,因为有子类重写了父类,所以在编译期是无法得知这个sayHello()到底是哪个版本吧? 只有到了运行期,Sub s=new Sub(); 因为继承,所以s继承了方法f();那么这个this是当前对象的引用,也就是父类的引用,指向子类对象的实例 .. 所以在运行期  这个this就相当于是Base b=s;所以很自然的,这个运行的结果是子类版本的sayHello();