黑马程序员技术交流社区

标题: 关于多态向上转型的一个疑问 [打印本页]

作者: Rockray    时间: 2013-10-11 20:05
标题: 关于多态向上转型的一个疑问
本帖最后由 Rockray 于 2013-10-11 21:29 编辑

代码:
  1. class Animal {
  2.     public void eat(){
  3.         System.out.println("动物");
  4.     }
  5. }
  6. class Cat extends Animal {
  7.     public void eat() {
  8.         System.out.println("吃鱼");
  9.     }
  10.     public void catchmouse() {
  11.         System.out.println("抓老鼠");
  12.     }
  13. }
  14. class Dog extends Animal {
  15.     public void eat() {
  16.         System.out.println("吃骨头");
  17.     }
  18.     public void kanjia() {
  19.         System.out.println("看家");
  20.     }
  21. }
  22. class DuoTaiDemo{
  23.     public static void main(String[] args){
  24.         Animal c = new Cat();
  25.         c.eat();
  26.     }
  27. }
复制代码
为什么输出的是“吃鱼”,而不是“动物”呢?

这句代码
  1. Animal c = new Cat();
复制代码
我的理解是 new一个 Cat 类型的对象,再把它转为它的父类类型,即 Animal 类型,调用的时候是调用 Animal 类型的方法,但是实际不是这样。
(1)如果理解成向上转型为父类 Animal 类型,则 c.catchmouse()  是错误的,因为父类 Animal 中没有 catchmouse() 方法。
(2)如果理解成向上转型后 实际还是个 Cat 类型的对象,那个调用 c.eat() 方法时就应该输出 “动物”,可实际结果是 “吃鱼”。
求大神告诉我到底该怎么理解???

作者: 赖波    时间: 2013-10-11 20:38
1.将一个父类的引用指向一个子类对象,称为向上转型,自动进行类型转换。
2.此时通过父类引用变量调用的方法是子类覆盖或继承父类的方法,不是父类的方法。
3.父类引用变量无法调用子类特有方法
作者: Rockray    时间: 2013-10-11 20:51
赖波 发表于 2013-10-11 20:38
1.将一个父类的引用指向一个子类对象,称为向上转型,自动进行类型转换。
2.此时通过父类引用变量调用的方 ...

能详细点吗,怎么理解起来好些呢?
作者: 张慧    时间: 2013-10-11 20:59
c.catchmouse()  在编译时就是不通过的。因为编译时只是检查一些语法错误。在Animal中没有这个方法,所以就报错了。

Animal c = new Cat();这句话其实就是将引用c指向 创建的一个Cat对象的内存地址。所以当c在调用Cat里面的方法时,在Cat中有这个方法就直接调用了。如果没有,就去找父类中的方法(这个就是继承了)。

如果你看到了反射,那么可以看下下面这个代码:
  1. public static void main(String[] args) throws Exception{
  2.                Animal c = new Cat();

  3. //               c.catchmouse();//错误
  4.                Class clazz = Cat.class;//得到Cat字节码对象
  5.                Method m = clazz.getDeclaredMethod("catchmouse", null);//得到catchmouse方法、
  6.                m.invoke(c, null);//调用c的catchmouse()
  7.           }
复制代码
上面代码,其实就是通过反射调用了c中的catchmouse(),就是你想用 c.catchmouse()  去实现的效果。
作者: Rockray    时间: 2013-10-11 21:08
本帖最后由 Rockray 于 2013-10-11 21:10 编辑
张慧 发表于 2013-10-11 20:59
c.catchmouse()  在编译时就是不通过的。因为编译时只是检查一些语法错误。在Animal中没有这个方法,所以 ...

还没看到反射,不太懂。
我现在是这样理解的:
Animal c = new Cat() ;
左边这个父类引用类型,定义了有哪些成员函数;右边的 Cat 型的对象,定义了每个函数的具体实现。
引用的时候先在父类中找有没有定义调用的成员函数,如果没有就编译失败。
如果父类有调用的成员函数,在看子类有没有重写这个函数,若重写则调用子类中的具体实现,若没有重写则调用父类中的具体实现。
你看我这样理解的对吗??
谢谢!
作者: 赖波    时间: 2013-10-11 21:13
Rockray 发表于 2013-10-11 20:51
能详细点吗,怎么理解起来好些呢?

第一句话应该没问题,小盒子能放进大盒子的道理。
第二句话,因为是引用,引用,就如我们说的,引用哪个名人的一句话……。是名人的话,引用子类是子类的
第三句话,因为父类没子类特有方法,自己不知道,别提调用了,他根本就不知道有没有。
作者: 张慧    时间: 2013-10-11 21:25
Rockray 发表于 2013-10-11 21:08
还没看到反射,不太懂。
我现在是这样理解的:
Animal c = new Cat() ;

差不多吧,编译看等号左边,运行看等号右边。

Rockray :“如果父类有调用的成员函数,在看子类有没有重写这个函数,若重写则调用子类中的具体实现,若没有重写则调用父类中的具体实现。”

其实你这么说我感觉不太准确。应该是先找子类中的方法,因为运行看等号右边嘛,如果有,那就直接调用就好了;如果没有再找他的父类。你可以把对象内存这块再看下,或许就更好理解了。

我是这么理解的。:)

作者: Rockray    时间: 2013-10-11 21:28
张慧 发表于 2013-10-11 21:25
差不多吧,编译看等号左边,运行看等号右边。

Rockray :“如果父类有调用的成员函数,在看子类有没有重 ...

嗯,好的,谢谢
作者: Rockray    时间: 2013-10-11 21:29
赖波 发表于 2013-10-11 21:13
第一句话应该没问题,小盒子能放进大盒子的道理。
第二句话,因为是引用,引用,就如我们说的,引用哪个名 ...

嗯,理解了,谢谢




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2