黑马程序员技术交流社区

标题: 求关于多态中声明、访问、调用的理解和方法 [打印本页]

作者: 夏夜猫    时间: 2013-8-26 15:01
标题: 求关于多态中声明、访问、调用的理解和方法
本帖最后由 杨增坤 于 2013-8-27 22:13 编辑

基础班面向对象部分的课已经上完了,昨天针对这部分内容进行了一次考试,但是结果感觉很糟糕,因为之前熬夜复习就正好卡在了多态这一块儿。

考试的内容很多是用到多态知识点的,一旦涉及到调用访问,就会很晕,旁边同桌做完卷子时我才做到一半,主要就是对于多态这部分的题目卡住了太长时间,最后还在不确定的情况下填上一个答案跳过去...

例如下面的这道题目:

class A {
       public int i = 10;
}


class B extends A{
       public int i = 20;
}


public class Test{
       public static void main(String args[]){
       B b = new B();
       A a = b;      
       System.out.println(b.i);
       System.out.println(a.i);
   }
}


输出为多少 ( )
A.10 10   
B.10 20     
C.20 10   
D.20 20

以及这道题目:

给定如下Java程序代码,在横线处加入( )语句,可以使这段代码编译通过。
interface Parent{
      public int count(int i);
      }


public class Test implements Parent {
      public int count(int i){
           return i % 9;
      }

      public static void main(String[] args){
        ________________
        int i = p.count(20);
       }
}


A. Test p = new Test();
B. Parent p;
C. Parent p = new Parent();
D. Test p = new Parent();


感觉应该是遇到的最基本但也是最常见的两种题目类型了(继承多态和接口多态),这样的题目(尤其是红色字体部分)不仅考试时遇到,平时老师上课时、自己做题时都会经常遇到,而真正不懂的地方也正是这里。

上面的两道题倒不一定要求出答案(我更希望自己弄懂以后能独立做出来),答案不重要,重要的是我得知道为什么选。

自己整理的思路遇阻疑惑有下:

1.重写和重载是不是一种多态的体现?

2.类B继承类A,子类B具有父类A的形态;类D是接口C的实例化,类D具有接口C的形态。这里的形态指的是什么?

3.继承多态在声明或调用时有什么要注意的,具体怎么操作?接口多态呢?
     3.1 什么是访问,什么是调用?访问和调用分别用在哪里?(继承和接口中是否一样?)
     3.2 访问和调用如何使用?(继承和接口中是否一样?)

4.我的课堂笔记上关于多态的访问记有以下一段,现在回顾时却不知道怎么去理解,我感觉是一个关键,请帮我看下这段是什么意思:
     父类类型 变量名 = 子类对象;
     变量名.调用成员变量 → 声明类型
     变量名.调用成员方法 → 看实现内存

5.老师总结过一句话叫做“编译看声明,运行看实现。”,该如何理解?

大概就是以上东西了...这一块真的很乱,我最近每次做题都是不断的“a.fn();   a.Test;  b.fn();   b.A;  ....”这样不断的修改,编译运行后看结果,确定要用的是哪个,可是知道了正确的答案,却不知道怎么回事这样写就实现了,刚刚那样写为什么就错了?

关于多态这一块的理解和运用,希望有大神可以帮帮我,这个真的不知道怎么去理解,经常面对着一道题几小时试着去理解,可还是很乱,非常焦虑,因为面向对象的内容已经讲完了,可是还有这么大一片空白。

可以通过给我讲解上面的题目举例教我,可以给我说说您们理解这块的思路借我参考,也可以总结帮我理顺,总之,我真的很想把这块知识给弄明白...
{:soso_e183:}{:soso_e183:}{:soso_e183:}

作者: 清心玉质    时间: 2013-8-26 16:35
本帖最后由 清心玉质 于 2013-8-26 16:39 编辑

第一个题答案:20 10
题中考的是多态中成员变量的特点:无论编译和运行,都参考左边(引用型变量所属的类)、,也就是说,对于继承类中的同名变量,用引用变量若用子类定义 ,那么成员变量的值就是子类定义的值,用父类接收,成员变量就是父类定义的值。

容易和多态中成员方法的特点混淆:
编译时参阅引用型变量所属的类中是否有调用的方发,如果没有编译失败,而运行的时候都是运行实例所属的类中的方法,即:不管左边,只要编译通过,运行的都是右边的方法。

例如:
class A {
int i=10;
void pp(){
System.out.println("fu");
}
}
class B extends A{
int i=20;
void pp(){
System.out.println("zi");
}
}

A a = new B()
B b = new B()
a.pp()和b.pp()输出的都是子;a.i = 10,参考引用变量所属类 A 中i;b.i=20,参考引用变量所属类 B中i;

第2题,写了实现类实现接口,子类重写父类方法,创建实现类对象,调用实现类对象方法,所以应该是A

作者: gulup    时间: 2013-8-26 17:03
本帖最后由 gulup 于 2013-8-26 17:12 编辑

你感觉会乱,是因为面向对象的思想还没有形成,这个不能急,只能靠自己慢慢理解,有些人一听就能理解,有些人一个月都理解不了。
回过头来回答你的问题。
1.重写和重载是不是一种多态的体现?
面向对象中,多态这个特性主要表现就是在两方面
1,方法的重写和覆盖(我比较喜欢这样叫,比较不会容易混淆)
2,对象的多态性(这个主要就是说对象的上下转型)

2.类B继承类A,子类B具有父类A的形态;类D是接口C的实例化,类D具有接口C的形态。这里的形态指的是什么?
什么叫多态?就是多种形态。一个人你能认出他,主要是因为他的外貌,名字等,这些就是这个人的形态,他的儿子,肯定会像他,而且肯定是和他一个性的,所以说他儿子就具有了他爸爸的形态。一个类A,具有各种方法和属性,这些方法和属性,就是这个类的形态的构成,当另一个类B继承了这个类之后,就拥有了类A的所有属性和方法,那么如果这个类B不去扩展任何属性和方法的前提下,它除了类名和类A不一样,其他有区别吗?没有。所以说一个类继承了另一个类之后,就具有了另一个类的形态。那什么叫多?当子类扩展了它自身的属性和方法等,那么它就不仅具有父类的形态,它还具有了自己的形态,那就是多种形态。它可以向上转型为父类。这就是多态。接口同理。

3.继承多态在声明或调用时有什么要注意的,具体怎么操作?接口多态呢? 回到第一题,多态主要就表现在方法和对象中。不管你是继承还是实现。当你实现了方法覆盖,重写,或者对象的转型。你就具体操作出了多态。      
3.1 什么是访问,什么是调用?访问和调用分别用在哪里?(继承和接口中是否一样?) 访问和调用,其实可以差不多,也可以说不一样。你想想,一个人,你需要知道他的名字,你可以直接问他,他告诉你他的名字了,这时候可以称之为你访问了他的名字这个属性吧?同时,其实你是在调用他一个叫做讲名字的方法,他通过这个方法来告诉你的。      
3.2 访问和调用如何使用?(继承和接口中是否一样?)上面的比喻说完了,相信你应该能理解访问和调用。我要访问一个类中的某个属性,其实也称之为我调用了其属性。继承和接口到底有啥不同?JAVA因为只支持单继承,为了弥补这个缺陷,就有了接口实现。多重实现,实现多个接口,以达到类似多重继承的目的。继承会继承父类的所有方法和属性,父类中一般都会实现好这些元素,一旦父类的一些东西是你不满意的,你想修改就会不灵活。接口的实现,因为接口里面都是常量和抽象方法,需要子类自己去做实现,灵活多变,接口更倾向于作为一个标准使用。因为多态,我可以向上转型为接口类型,那我的某个方法就可以直接设计成接受接口类型作为参数,把实现了接口的子类传入,不同的子类有不同的实现,这也是多态的表现。如果要继承,也应该继承抽象类。千万不要去继承一个已经实现好所有方法和属性的类。因为这个类你已经实现好了,当你继承了它,那么你可能避免不了的就会去覆盖掉了某些方法,多态下,调用的,肯定是被覆盖的方法,那么,那个父类已经实现好的方法就等于做了无用功。   

4.我的课堂笔记上关于多态的访问记有以下一段,现在回顾时却不知道怎么去理解,我感觉是一个关键,请帮我看下这段是什么意思:      父类类型 变量名 = 子类对象;      变量名.调用成员变量 → 声明类型      变量名.调用成员方法 → 看实现内存
抱歉,这个我解释不了。  

5.老师总结过一句话叫做“编译看声明,运行看实现。”,该如何理解
这句话我也不是很能理解,我的理解是,例如,B继承了A B b = new B(); A a = b; 编译时,a是A类型,b是B类型。 当类被加载到虚拟机中,在内存中分配内存的时候,A类型的a指向了B类型的b,那么它现在是否可以成为有A形态的B?这时候如果你要调用a的方法,那么是否就要看B类型具体的实现了?好吧,这里解释得太生硬了。。
作者: 夏夜猫    时间: 2013-8-26 20:56
清心玉质 发表于 2013-8-26 16:35
第一个题答案:20 10
题中考的是多态中成员变量的特点:无论编译和运行,都参考左边(引用型变量所属的类) ...

首先谢谢。这个举例题的说明我看的思路畅快无比。
想追问一下,首先,你说的“对于继承类中的同名变量,用引用变量若用子类定义 ,那么成员变量的值就是子类定义的值,用父类接收,成员变量就是父类定义的值”,红色字和下划线部分,分别是什么意思?能再举个例子么?

另外,关于声明的一些问题,例如你举例中声明的是
A a = new B();
B b = new B();
那如果声明
A a = new A();
B a = new A();
可以么?是编译报错还是可以声明,如果能声明,是否能调用?他们的调用关系又是怎样的?

麻烦您有时间再帮我解决下。。再次谢谢。。


作者: 夏夜猫    时间: 2013-8-26 21:45
本帖最后由 夏夜猫 于 2013-8-26 21:47 编辑
gulup 发表于 2013-8-26 17:03
你感觉会乱,是因为面向对象的思想还没有形成,这个不能急,只能靠自己慢慢理解,有些人一听就能理解,有些人一 ...


首先谢谢你的帮助解答。
想向您追问一下内容:
1.覆盖和重写的区别
class A {
    int i=10;        
    void p1(){
    System.out.println("fu");
    }
}
class B extends A{
    int i=10;          1
//int i =1;            2
//int j =10;          3
    void p1(){
    System.out.println("fu");      4
    }
/*
    void p1(){
    System.out.println("zi");        5
    }
    void p2(){
    System.out.println("fu");       6
    }

    void p2(){
    System.out.println("zi");        7
    }
*/
}
上面的式子中,1、2、3哪种情况是对int i=1的重写,哪种是覆盖?

4、5、6、7哪种情况是对  void p1(){                                 的重写,哪种是覆盖?
                                                 System.out.println("fu");
                                         }





2.关于调用,比如类A继承(或实现)类(接口)B,
A a = new A;    A a = new B;    B b =new A;   B b =new B;
a.A;    a.B;    b.A;    b.B;
这些组合在一起时究竟该怎么用呢?



作者: gulup    时间: 2013-8-26 22:59
1-属性没有重写个覆盖这一说法,方法才有。
多态下,调用的到底是子类当中修改过的同名属性还是父类中的属性。需要看该对象的具体类型。
给你一段代码,希望你能看懂。
  1. class Test {
  2.         public static void main(String[] args) {
  3.                 /*等号左边为A类型,等号右边为B类型,这种写法等效于下面这样:
  4.                 * B b = new B();
  5.                 * A a = b;
  6.                 * 这个就是对象的向上转型,子类转为父类类型。当对象向上转型后。调用a对象的属性时,使用的都是父类的属性。
  7.                 * 调用的方法,是子类的方法。1处输出的结果就是B和10
  8.                 */

  9.                 A a = new B();
  10.                 a.p();                //B
  11.                 System.out.println(a.i);                //10

  12.                 /*等号左边为A类型,等号右边为B类型,这种写法等效于下面这样:
  13.                 * B b = new B();
  14.                 * A a = b;
  15.                 * 下面的B b = (B)a1;是对象的向下转型。需要添加强制类型转换。
  16.                 * 注意,向下转型是必须先有了向上转型之后才可以发生的。
  17.                 * 如果你这里直接这样写:
  18.                 * B b = (B)new A();或者:
  19.                 * A a = new A();
  20.                 * B b = (B)a();
  21.                 * 这两种2做法都是错误的。
  22.                 * 方法的调用,不用说,都是调用子类的。关键是类中的属性。
  23.                 * 因为你把b的类型从A强制转换成了B。所以调用的就不再是A类中的属性。
  24.                 * 最后的输出结果分别是B和20。
  25.                 */
  26.                 A a1 = new B();
  27.                 B b = (B)a1;
  28.                 b.p();                //B
  29.                 System.out.println(b.i);                //20
  30.         }
  31. }

  32. class A{
  33.         int i = 10;
  34.         public void p(){
  35.                 System.out.println("A");
  36.         }
  37. }

  38. class B extends A{
  39.         int i = 20;
  40.         public void p(){
  41.                 System.out.println("B");
  42.         }
  43. }
复制代码
2:
A a = new A();(直接就是一个A类)  
A a = new B();(对象的向上转型,这时候其实它就是一个B类型)
B b = new A();(不能直接这样子实例化对象,必须要先有向上转型发生,具体看上面代码注释)
B b = new B();(这个没啥好说,就是一个B类型)

再有不明白的话继续问,我尽量回答
作者: 夏夜猫    时间: 2013-8-26 23:05
gulup 发表于 2013-8-26 22:59
1-属性没有重写个覆盖这一说法,方法才有。
多态下,调用的到底是子类当中修改过的同名属性还是父类中的属 ...

这段代码不但能看懂,而且真的就是那种哪里痒挠到哪里的舒畅感觉,方不方便把题目也包含在内完整贴出来,我存一下好好看一遍加深印象,一直不知道怎么描述心里面的疑惑,突然知道了,感觉很好,谢谢谢谢谢!!!!!

作者: 夏夜猫    时间: 2013-8-26 23:09
gulup 发表于 2013-8-26 22:59
1-属性没有重写个覆盖这一说法,方法才有。
多态下,调用的到底是子类当中修改过的同名属性还是父类中的属 ...

抱歉,没注意,题目就是那段代码下面的内容,刚刚发现,谢谢了!




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