大家好,今天在重温基于继承的多态时遇到了这样的一个问题,很不解,特来请教:public class Test {
public static void main(String[] args) {
Fruit fruit = new Apple();
System.out.println("Here the name is " + fruit.name);
System.out.println("Again the name is " + fruit.getName());
                  System.out.println("3rd the name is " + fruit.getNameAgain());
}
}class Fruit {
String name = "fruit"; String getName()
{
return this.name;
}         String getNameAgain()
         {
                  return this.name;
         }}class Apple extends Fruit {
String name="Apple";         String getNameAgain()
         {
                  return this.name;
         }
}执行结果为:
Here the name is fruit
Again the name is fruit
3rd the name is Apple我之前所学的概念中,对于成员变量,子类对象会隐藏父类对象中的成员变量,当用父类引用fruit指向子类对象apple的时候,直接使用fruit.name返回的是父类中的"fruit"我可以理解,执行的第一句也就没有问题。第三局执行也没有问题,因为子类中重写了父类的getNameAgain()方法,多态调用方法的时候认对象类型而不是引用类型。 但是第二句就不解了,在fruit类中定义的getName()中使用的是this关键字,this这个关键字是和具体的运行是对象关联而与引用类型无关的,在程序运行中明明创建的是一个apple子类对象,虽然是用父类的引用来指向这个对象的,但是毕竟在内存中存在的还是apple子类对象啊,虽然apple没有重写getName(),但是getName()中使用的是this关键字啊,而多态调用方法的时候认对象类型而不是引用类型。为什么执行出来的结果却是返回的父类中的name值?还请各位指点下迷津了,非常感谢~~~

解决方案 »

  1.   

    String getName()
    {
    return this.name;
    }this.name = fruit.name
    fruit.name = fruit这样可以理解吗?
      

  2.   


    this指向的是运行时创建的对象实例,例子中创建的对象并不是fruit而是apple,只是用的fruit引用变量来引用对象而已,this指向的应该是apple对象啊,而apple对象中的name已经将父类的name隐藏了的啊
      

  3.   

    又是这种问题,属性是不能重写而方法可以重写,因为属性是静态编译,方法是动态编译
    对于
    class Fruit {
    String name = "fruit";  //编译生成的.class,name是一个相对地址,
                               //即程序加载时在数据段给name分配一个空间,
                               //这块空间的地址就是这个相对地址String getName() //这里会有个方法地址
    {
    return this.name; //所以这里的字节代码相当于返回 相对地址的内容,这个地址是写死的相对地址
                        //也就是上面编译时给name指定的那个地址
    }  String getNameAgain() //这里会有个方法地址
      {
      return this.name; //这里情况一样
      }}
    好了,下面来看子类
    class Apple extends Fruit {
    String name="Apple"; //这里也是给name分配一个地址  String getNameAgain() //这里会有个方法地址,注意这个方法是重写父类的方法
      {
      return this.name; //这里也是相当于返回name的相对地址的内容,
                           //这个地址在字节代码里也是写死的,也就是name的相对地址
      }}
    首先要知道,编译出来的2个class文件,所以对于上面所说的,两个class字节代码中的name的相对地址是不可能一样的,因为子类会先调用父类构造器为父类分配空间,然后再为自己分配空间,所以从内存空间上看,父类的name和子类的name是不可能地址一样的。
    接下来看主程序
    public static void main(String[] args) {
    Fruit fruit = new Apple();
    System.out.println("Here the name is " + fruit.name); //上面说了,name已经被编译为一个地址,
                                                                 //所以这里直接取地址的内容,那么取哪个呢?
                                                                 //看fruit是什么类型的,是Fruit,所以取的是Fruit的name的信息
    System.out.println("Again the name is " + fruit.getName());//这里会先在虚拟表中查找getName方法,
                                                                        //找到方法的入口在Fruit的class里(因为子类没重写)
                                                                        //于是调用Fruit的getName,上面说了,getName的this.name是个写死的相对地址(父类的,因为在父类的class文件里),
                                                                        //也就是说返回Fruit的name的相对地址的内容
      System.out.println("3rd the name is " + fruit.getNameAgain()); //这里会先在虚拟表中查找getNameAgain方法
                                                                        //找到方法入口在Apple的class里(因为子类重写了,对于虚拟表的相同的key,只能有一个value,
                                                                    //这个value是子类后来分配内存时覆盖掉的)
                                                                    //于是调用Apple的getNameAgain方法,getNameAgain的this.name是个写死的相对地址(子类的,因为在子类的class文件里)
                                                                    //也就是说返回Apple的name的相对地址的内容
    }
      

  4.   

    非常感谢qybao,呵呵,你是直接从底层的角度来解释的就很清楚了。呵呵,我学Java和现在回顾Java看的《Java SE 6.0编程指南》,里面都是从Java表象上来谈的,比方说对于成员变量说的是只能隐藏不能重写,对方法则是重写,但是为什么就没有提及了。所以有些地方看似明白了,但是换一个环境下就还是不太明白。
    Thanks a lot