A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

© 我是松鼠 初级黑马   /  2013-5-18 17:32  /  2136 人查看  /  9 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

本帖最后由 我是松鼠 于 2013-5-18 22:59 编辑

如图所示,编译结果是:在Person类中找不到talk()方法,我想弄明白的是,我如果把红线的那个p.talk();删除,运行结果是“为什么”。也就是说p调用的是子类中的say()方法,那么p就应该是属于Student类啊。那为什么又找不到Student类中的talk()方法了呢?
Person P=new Student()属于向上转型,也就是说把子类转化为父类,那么完成这句语句后p到底是属于父类还是子类呢?
另外就是强制转换问题:比如Student s=(Student)t,假如t是Person类的对象,那么完成这个强制转换之后,t是属于Person类还是Student类呢?(PS:经过我尝试,显示t应该还是属于Person类,但是为什么呢?明明已经强制转换了啊?)求高人解答,在线等,万分感谢

还有个题外话,我前两天发的一个问题帖怎么被删了,账号好像也被封了,我又没发啥不该发的,现在正在自学基础,希望过两个月能去黑马培训。

QQ图.jpg (35.41 KB, 下载次数: 0)

QQ图.jpg

评分

参与人数 1技术分 +1 收起 理由
袁梦希 + 1

查看全部评分

9 个回复

倒序浏览
本帖最后由 袁梦希 于 2013-5-18 17:50 编辑

你的UID 挺亮的,四个5  

根据多态的特点,如果是方法,编译时看的是左边,运行时看的是右边。
Person p = new Student();
编辑的时候他会检查P中有没有say()方法,如果有就没问题,如果没有就会发生编译错误。
当运行的时候呢,执行的是Student对象中的say()方法  也就是你覆盖的。就是说运行的是等号右边的
所以你输出的是“为什么”。

但是你的第二个问题  我不知道你的t变量是怎么来的,你代码里面也没有,
所以无法解答.

[fly]黑马云青年为您手动解答[/fly]

评分

参与人数 1技术分 +1 收起 理由
刘胜寒 + 1

查看全部评分

回复 使用道具 举报
建议你在看看毕向东老师的多态的视频。
我还是把总结给你吧!
回复 使用道具 举报
袁梦希 发表于 2013-5-18 17:38
你的UID 挺亮的,四个5  

根据多态的特点,如果是方法,编译时看的是左边,运行时看的是右边。

团长,你下面的字怎么还能动啊,菜鸟跟团长跑
另外,不能老待在一个林子的,所以到处去串串门,看看世界:victory:
回复 使用道具 举报
关于第二个强制转换的问题,举个例子来说,如下覆写equals方法的程序中,编译结果是:在Object类中找不到name和age属性,但是此时明明已经将obj转化成Person类了,为什么还会在Object在、类中找呢?这个程序中如果将红线部分即obj替换为per则可正常运行,不懂啊。。。。还有谢谢你的回答

回复 使用道具 举报
刚才图片忘了,不好意思

1.jpg (57.93 KB, 下载次数: 0)

1.jpg
回复 使用道具 举报
  1. 1,多态产生的原因
  2.   什么是多态了?一种事物有多种状态。这是对多态的通用解释,在Java中的多态又是如何的了?要了解多态,先从它产生的原因说起。
  3.   相信看到文章的你清楚的明白 javac 和 java 这两个命令了,一个是编译java源文件,一个是运行编译后的文件。在解释清楚产生的原因前,我先创建几个类:Animal(动物) Cat(猫咪) Dog(汪星人)。其中Cat Dog 都继承自 Animal。
  4.   Animal  foo = new Animal();
  5.   Cat niki = new Cat();
  6. 这是我们熟悉的创建对象的方式,上面提到的:java有编译和运行两阶段。对于上述语句在表达式左边的类型(Animal)是 foo的编译期类型;在运行阶段,JVM会检查表达式右边的类型,也是Animal。对于第二句也是同样的道理。
  7. 通俗的说法:你要一个动物(编译) ,我给你一个动物(运行(具体是什么不得而知,可以肯定它一定要是动物));你要一只小猫(编译),我给你一只小猫(运行)。逻辑上是没问题的。现在情况是这样的,我要一个动物,你给我一只小猫,这个逻辑也行的通,但是它隐含的条件是猫的确是动物,你给我一个玩具猫那可不行,从分类上讲玩具猫是玩具。
  8. 通过上述表达,我们再来看看java中多态产生的原因:具有继承关系的类之间,对于同一对象的引用,编译期间和运行期间的类型不相同。代码描述如下:
  9. Animal foo = new Cat();
  10.   2,向上转型
  11. 对于
  12. Animal foo = new Cat();
  13. 编译期间,编译器会检查 foo 这个引用的类型:为Animal。但是在实际运行时,我们new 的是一个Cat类型的实例对象,那么foo引用指向的是一个Cat 类型的对象。为了解决这种情况,JVM会将这个Cat类型提升为Animal类型,这就是向上转型。
  14. 上述情况可简述:子类对象赋给父类引用,编译器会完成向上转型。向上转型是建立在继承树上的,之所以称为向上转型,是因为引用类型在继承树是由下往上移动的。

  15. 在继续阅读之前,我们需要了解通常的说法,以免有的读者感到困惑。
  16. 静态方法--类方法,用static 关键字修饰,专属于类,不依赖于对象。
  17. 非静态方法--实例(对象)方法,无static关键字修饰,依赖于实际创建的对象。
  18. 下面是代码:
  19. class Animal  //定义一个Animal类
  20. {
  21.         public void eat(){  //定义了eat()和cry()两个实例方法
  22.                 System.out.println("they eat what they eat");
  23.         }
  24.         public void cry(){
  25.                 System.out.println("animals may cry!");
  26.         }
  27. }
  28. class Cat extends Animal  //定义一个Cat类继承自Animal
  29. {
  30.         public void eat(){  //覆盖了父类的 eat() 和 cry()方法
  31.                 System.out.println("吃鱼");
  32.         }
  33.         public void cry(){
  34.                 System.out.println("喵!");
  35.         }
  36.         public void name(){  //定义自己特有方法 name()
  37.                 System.out.println("niki");
  38.         }
  39. }
  40. class Dog extends Animal  //定义一个Dog类,继承自Animal
  41. {
  42.         public void eat(){   //覆盖了父类的 eat() 和 cry()方法
  43.                 System.out.println("啃骨头");
  44.         }
  45.         public void cry(){
  46.                 System.out.println("汪汪!");
  47.         }
  48.         public void name (){   //定义自己特有的方法 name()
  49.                 System.out.println("coco");
  50.         }
  51. }
  52. public class Demo
  53. {
  54.         public static void main(String args[]){
  55.                 Animal foo = new Animal();//引用类型与对象类型一致
  56.                 Animal cat = new Cat();  //引用类型与对象类型不一致
  57.                 Animal dog = new Dog();  //引用类型与对象类型不一致
  58.                 /*Dog dog = new Cat();
  59.                 显然你不能这么干,从实际生活情况考虑:我要一只狗,你给我一只猫。
  60.                 从语法上说,它们的类型不兼容,因为它们之间无继承关系。当然你也
  61.                 可以让猫这个类继承狗来完成向上转型,但是这是很奇怪的一个逻辑,
  62.                 语法上是能通过的。
  63.                 */

  64.                 foo.eat();   
  65.                 foo.cry();

  66.                 cat.eat();
  67.                 cat.cry();
  68.                 //cat.name();  无法通过编译

  69.                 dog.eat();
  70.                 dog.cry();
  71.                 //dog.name();  无法通过编译


  72.         }
  73. }
  74. ---------- 运行java程序 ----------
  75. they eat what they eat
  76. animals may cry!
  77. 吃鱼
  78. 喵!
  79. 啃骨头
  80. 汪汪!
  81. ------------------------------------
  82. 先来说说无法通过编译的两句,在了解这以后对多态就有了进一步的了解。Animal dog = new Dog();在编译时,编译器会检查dog这个引用的类型:Animal;编译阶段编译器会认为它就是一个Animal类型,再来看我们没有通过编译语句:dog.name();编译器会去查找Animal这个类中是否有name这个方法。从Animal类的定义中,我们并没有看到name()方法,也就不指望编译器能通过了。这就告诉我们,多态的局限性,你只能使用父类中有的方法,子类特有的方法无法调用,因为编译阶段,子类对象还没哟产生,你无法预知子类对象有何种不同的方法。

  83. 解决了上述问题我们再来看输出结果为什么是这样的:cat.eat() ,这句能通过编译,cat引用去查找Animal类,发现有eat这个方法,但是输出的结果告诉我们,它实际调用的是Cat中的eat()方法,原因很简单。Animal cat  = new Cat();这一句我们一分为二的看,左边和右边,左边类型是编译期类型,右边是运行期引用实际指向的对象。在编译期,cat引用是Animal类型,查找到eat()方法,运行时,创建的实际是Cat对象,就如同开始提到的实例(非静态)方法,依靠的是对象本身,这也就解释了为何输出的是上述结果。

  84. 这只是对多态的一个初步认识,针对的是实例(非静态)方法。现在总结如下:

  85. 在编译时期:参阅引用类型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有编译失败。
  86. 在运行时期:参阅对象所属的类中是否有调用的方法

  87. 这里很明显我们并没有提到:成员变量 和 静态方法。需记住这一下两点
  88. 在多态中
  89. 成员变量:编译和运行,都参考左边(引用类型变量所属的类)
  90. 在多态中
  91. 静态成员函数的特点:编译和运行,都参考左边
  92. 4,向下转型
  93.    有向上转型,就有向下转型。再来看看我们上面的代码片段:
  94. Animal cat = new Cat();
  95. //cat.name() 无法通过编译
  96. 现在的情况又改变了:我们知道在编译阶段cat引用的类型是Animal,如果想要调用Cat类中特有的name()方法,该如何实现了?这里就用到了向下转型了。你可以强制性的这样做:
  97. Cat anotherCat = (Cat)cat;
  98. anotherCat.name();
  99. 我们将cat引用的类型从Animal强制转换为Cat,并将引用赋给另外一个引用。最后去调用name()方法。但是这里有一个问题,这里能做强制转换,是因为我们明确的知道我们new 的是Cat对象,在做向下转型时也就很明确的知道转成Cat类型,虽然Dog类也是Animal的子类。但是这种做法并不明,有时候并不确定它们在继承树中是否有关系,可以看看下面这段代码:
  100. class Animal
  101. {
  102.         public void eat(){
  103.                 System.out.println("they are what they eat");
  104.         }
  105. }
  106. class Dog extends Animal
  107. {
  108.         public void eat(){
  109.                 System.out.println("fash");
  110.         }
  111. }
  112. class Cat extends Animal
  113. {
  114.         public void eat(){
  115.                 System.out.println("bone");
  116.         }
  117. }
  118. class Toy
  119. {
  120. }
  121. class Boat
  122. {
  123. }
  124. public class Demo2
  125. {
  126.         public static void main(String args[]){
  127.                 fun(new Animal());
  128.                 fun(new Cat());
  129.                 fun(new Dog());
  130.                 fun(new Toy());
  131.                 fun(new Boat());
  132.         }
  133.         public static void fun(Object obj){
  134.                 if(obj != null && obj instanceof Animal){
  135.                  Animal foo = (Animal)obj;
  136.                                          foo.eat();
  137.                 }
  138.                 else
  139.                         System.out.println("not animal!");        
  140.         }
  141. }
  142. ---------- 运行java程序 ----------
  143. they are what they eat
  144. bone
  145. fash
  146. not animal!
  147. not animal!
  148. ------------------------------------


  149. fun(new Animal());
  150. 当调用这个函数时,我们传入的是一个Animal类型的引用,但是fun函数在编写时要求接受的参数类型是Object,由于Object是所有类的父类,Animal对象的引用自动向上转型为Object,完成参数的传递。此时形式参数obj 的类型是Object指向的是Animal类型的实例对象:
  151.                  Object obj = new Animal();
  152. 参数传递的过程可以理解成这样。再来看:
  153.                  if(obj != null && obj instanceof Animal)
  154. 首先要保证obj指向了某个对象,后面的 obj  instanceof Animal 可以表述为
  155.    obj (指向的实例对象)是 Animal 这个类的实例对象吗?instance(实例)of(是谁的) 这就是 instanceof 关键字的组成,a instanceof  X :前者是后者的一个实例对象吗?通过判断是obj 是Animal 的实例对象后,可以明确的进行强制类型转换了。至此,也就完成了向下转型。
  156.   
复制代码
回复 使用道具 举报
这是我自己写的一点总结  虽然写的不是特别好  但是我还是希望楼主能读下。我用尽量通俗的语言在说向上转型。你要看清楚 编译阶段 和  运行阶段 。
回复 使用道具 举报
不胖的胖子 发表于 2013-5-18 18:02
建议你在看看毕向东老师的多态的视频。
我还是把总结给你吧!

你写的总结非常好,非常感谢,请问你是在黑马培训的吗?
回复 使用道具 举报
我是松鼠 发表于 2013-5-18 19:38
你写的总结非常好,非常感谢,请问你是在黑马培训的吗?

看黑马的视频  然后结合一些书籍 。其实有时候停下来总结是不错的选择
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马