大家好,今天在重温基于继承的多态时遇到了这样的一个问题,很不解,特来请教: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值?还请各位指点下迷津了,非常感谢~~~
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值?还请各位指点下迷津了,非常感谢~~~
{
return this.name;
}this.name = fruit.name
fruit.name = fruit这样可以理解吗?
this指向的是运行时创建的对象实例,例子中创建的对象并不是fruit而是apple,只是用的fruit引用变量来引用对象而已,this指向的应该是apple对象啊,而apple对象中的name已经将父类的name隐藏了的啊
对于
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的相对地址的内容
}
Thanks a lot