写出以下代码的输出结果class A{
 public String f(D obj){return ("A and D");}
 public String f(A obj){return ("A and A");}
}
class B extends A{
 public String f(B obj){return ("B and B");}
 public String f(A obj){return ("B and A");}
}
class C extends B{}
class D extends B{}class test{
 A a1 = new A();
 A a2 = new B();
 B b = new B();
 C c = new C();
 D d = new D();
 System.out.println(a1.f(b));   A and A
 System.out.println(a1.f(c));   A and A
 System.out.println(a1.f(d));   A and D
 System.out.println(a2.f(b));   B and A
 System.out.println(a2.f(c));   B and A
 System.out.println(a2.f(d));   A and D
 System.out.println(b.f(b));    B and B
 System.out.println(b.f(c));    B and B
 System.out.println(b.f(d));   A and D
}
旁边的是标准答案,前三个都很好理解,但是后面三个真的是一头雾水啊,难道b,c都变成类A了?还有d,是不是如果子类里没有他的方法可调用,就看超类里是否有与他相对应的方法?
然后最后一个,b不是已经声明为B类了吗?为什么会调用A类里的方法呢?为什么结果不能是B and B呢?大家能帮忙给小弟解解惑吗?真是一头雾水啊!~~~~~~~~~~~

解决方案 »

  1.   

    楼主的问题我没有细看,但是我说的应该跟上面的相符
    父类(可以是类,也可以是接口)的对象可以通过实例化子类来产生,最容易理解的是父类为接口时,当要得到父类的对象时可以new 子类来产生
    例如:List list = new ArrayList();
      

  2.   

    这时超类对象可以调用子类的同名方法吗?
    这里 A a2 = new B(); 
         B b = new B();
                        ,但是 后面的输出为
    System.out.println(a2.f(b));-》B and A , 我觉得应该是 B and B才对呀
    而这里  System.out.println(a2.f(d));   A and D , a2又调用了超类里的方法 ,我觉得应该是 B and B才对
      

  3.   

    class A{
     public String f(D obj){return ("A and D");}
     public String f(A obj){return ("A and A");}
    }这个类编译能通过吗?
    方法前面是一模一样!!!!
      

  4.   

    好象是方法调用的优先问题this.f(o)
    super.f(o)
    this.f((super)o)
    super.f((super)o);
    按照这个在看看
      

  5.   

    楼上是正解,我再解释一下,首先先调用this.f(o),但如果子类覆写了父类的f(o),则调用subclass.f(o),这就是a2.f(b)和a2.f(c)调用B类的f(A obj),a2.f(d)调用class A的f(D obj)的原因。至于b.f(c)和b.f(d)是因为,首先查看this中有没有符合的方法,就查看super有没有符合的方法,再看this.f((super)o)如果没有符合,再看super.f((super)o)即楼上的顺序。
      

  6.   

    关于
     System.out.println(a1.f(b));   A and A
     System.out.println(a1.f(c));   A and A

     System.out.println(b.f(b));    B and B
     System.out.println(b.f(c));    B and B解释大家都没有异议吧 简单的方法调用 第一、二行 是new A()实例调用的是A.f(A obj)第三、四行 是new B()实例调用的是B.f(A obj)关于
     System.out.println(a2.f(b));   B and A
     System.out.println(a2.f(c));   B and A解释一般人会认为a2的实际型别是Ba2.f()根据java的动态方法调用机制,实际上调用的是B.f()而B.f有2个重载 就有点迷糊了。实际上 这是不是重载问题而是一个简单的覆盖问题 A.f(A obj)被 B.f(A obj)覆盖了a2.f(A obj)实际执行的就是是B.f(A obj)至于
    System.out.println(a2.f(d));   A and D
    因为A有准确的D型别方法f(D obj) 所以就调用A.f(D obj)
      

  7.   

    谁能给我解释一下下面这一句是为什么?谢谢!System.out.println(a2.f(b));   B and A
      

  8.   

    a2看成B的类型,而b又是B类型,不应该直接调用  B类的
    public String f(B obj){return ("B and B");    ???
      

  9.   

    觉得 
    piaopiao11() 
    jin_marshal(Sunnywolf)挺有道理的
      

  10.   

    经过试验觉得可能的原因如下:
    1)、a2是作为A来看待的
    2)、由于原因1),所以a2不能调用A中不存在的方法
    3)、B中的方法public String f(B obj){return ("B and B");}不存在于A类中,所以a2不会调用这个方法,不会输出"B and B"
    4)、但是B覆盖了A的方法为public String f(A obj){return ("B and A");}
    5)、a2本身其实是B类的实例,又4)中B的方法存在于A中,所以调用了public String f(A obj){return ("B and A");}
      

  11.   

    class A{
    public String f(D obj){return ("A and D");}
    public String f(A obj){return ("A and A");}
    }
    class B extends A{
    public String f(B obj){return ("B and B");}--不是多态,定义了一个新的成员函数
    public String f(A obj){return ("B and A");}
    }
      

  12.   

    public String f(A obj){return ("A and A");
    public String f(A obj){return ("B and A");
    override这帖子不看了  再看就要气暴了 
    就是一个简单的覆盖方法 还有这么多异议
      

  13.   

    楼上应该是对的
    System.out.println(a2.f(b));   B and A先进行方法的重载:
    a2 是被声明为A的,所以a2.f()找的是A类中的方法A.f()
    b是被声明为B的,B extends A,所以a2.f(b)先被确定为A.f(A)
    然后才是动态绑定:
    a2=new B(),a2的实际类型是B,所以绑定为B.f(A)
      

  14.   

    我就不明白:
     System.out.println(a2.f(b));   B and A
    中的b是B的一个对象,a2也是B的一个对象,
    而且B中也有public String f(B obj){return ("B and B");}
    为什么不调用上面的,而要调用下面的这个:
    public String f(A obj){return ("B and A");}不解,不知道各们是不是可以解释一下
      

  15.   

    因为a2是被声明为A的啊
    所以重载的时候把它看做A
    而动态绑定的时候才把它看做B
      

  16.   

    heh_621() ( ):
    因为a2是被声明为A的啊
    所以重载的时候把它看做A
    而动态绑定的时候才把它看做B你这解释也是有道理的,但是我按照上面那个仁兄的优先方法来想问题就有点说不过去了,其优先方法如下:方法调用的优先问题this.f(o)
    super.f(o)
    this.f((super)o)
    super.f((super)o);
    按照这个在看看
      

  17.   

    我再写个方法你可能就明白了:public static void f2(A a,A b)
    {   System.out.println(a.f(b));
    }然后调用的时候:f2(new B(),newB());
      

  18.   

    gcmj_java() 你看清楚了
    this.f(o)
    super.f(o)
    this.f((super)o)
    super.f((super)o);
    是在进行方法重载的时候的顺序,它并没有涉及方法的覆盖(动态绑定)顺序
      

  19.   

    a2.f(b)
    重载过程:
    this.f(o)------A.f(B),不存在的方法
    super.f(o)--------A无父类,不进行
    this.f((super)o)---------A.f(A) 找到,所以重载的结果是A.f(A)
    super.f((super)o);
      

  20.   

    "谁能给我解释一下下面这一句是为什么?谢谢!       System.out.println(a2.f(b));   B and A"
    输出本来就是 B and B
    a1为A的对象,a2是B的对象
    请看下面的程序:
    public class A{
     public String f(D obj){return ("A and D");}
     public String f(B obj){return ("A and B");}
     public String f(A obj){return ("A and A");}
     public String display(){return ("A的对象" );}
    }
    class B extends A{
     public String f(B obj){return ("B and B");}
     public String f(A obj){return ("B and A");}
     public String display(){return ("B的对象" );}
    }
    class C extends B{}
    class D extends B{}class test{
     
     public static void main(String args[]){
      A a1 = new A();
      A a2 = new B();//a2为B的对象
      B b = new B();
      C c = new C();
      D d = new D();
      System.out.println(a1.display());
      System.out.println(a2.display());
      System.out.println(b.display());
      System.out.println(c.display());
      System.out.println("a1.f(a2)   "+a1.f(a2));
      System.out.println("a1.f(b)   "+a1.f(b));
      System.out.println("a1.f(c)   "+a1.f(c));
      System.out.println("a1.f(d)   "+a1.f(d));
      System.out.println("a2.f(a1)   "+a2.f(a1));
      System.out.println("a2.f(b)   "+a2.f(b));
    System.out.println("a2.f(c)   "+a2.f(c));
    System.out.println("a2.f(d)   "+a2.f(d));
    System.out.println("b.f(a2)   "+b.f(a2));
      System.out.println("b.f(b)    "+b.f(b));
      System.out.println("b.f(c)    "+b.f(c));
      System.out.println("b.f(d)    "+b.f(d));
     }
    }
    /////////////////////////////////////////////////////////
    /////////////////////////////////
    ////////////////
    //////
    输出位:
    A的对象
    B的对象
    B的对象
    B的对象
    a1.f(a2)   A and A
    a1.f(b)   A and B
    a1.f(c)   A and B
    a1.f(d)   A and D
    a2.f(a1)   B and A
    a2.f(b)   B and B
    a2.f(c)   B and B
    a2.f(d)   A and D
    b.f(a2)   B and A
    b.f(b)    B and B
    b.f(c)    B and B
    b.f(d)    A and D
      

  21.   

    不明白输出的    a1.f(a2)   A and A
      

  22.   

    lingwen20(我人在江湖,江湖却没有关于我的传说...) ( ) 信誉:100    Blog  2006-11-17 2:11:47  得分: 0  
     
     
       
    其实这是多态的问题了~在多态中方法是后期才绑定的~~也就是只有在运行才能确定调用的方面
    我给你讲讲多态的调用规则吧,希望你能明白你的例子的输出缘由!!在多态中的调用有这样的规则:
    调用成员变量时,将调用引用类型所对应的类中的成员变量的,如果该类中没有定义的该变量,才会调用父类(有定义)的同名成员变量。调用成员方法时,将根据对象的运行时的类型来确定调用哪个成员方法,也就是说运行时类型是哪个类的引用,就调用哪个类中定义的成员方法楼上的说调用成员方法,那System.out.println("a1.f(a2)   "+a1.f(a2));中的a1.f(a2)应该算是运行类A的引用a1那调用方法也自然是调用A的成员方法,那么调用的方法应该是根据参数来确定吧,参数是a2(类B),那么应该是调用 public String f(B obj){return ("A and B");}才对.这是我的理解,请大家指点一下,实在迷惑
      

  23.   

    wanglb003() ( ) 信誉:100    Blog 
    "谁能给我解释一下下面这一句是为什么?谢谢!       System.out.println(a2.f(b));   B and A"
    输出本来就是 B and B
    a1为A的对象,a2是B的对象
    请看下面的程序:
    public class A{
     public String f(D obj){return ("A and D");}
     public String f(B obj){return ("A and B");}
     public String f(A obj){return ("A and A");}
     public String display(){return ("A的对象" );}
    }
    class B extends A{
     public String f(B obj){return ("B and B");}
     public String f(A obj){return ("B and A");}
     public String display(){return ("B的对象" );}
    }
    class C extends B{}
    class D extends B{}class test{
     
     public static void main(String args[]){
     A a1 = new A();
     A a2 = new B();//a2为B的对象
     B b = new B();
     C c = new C();
     D d = new D();
     System.out.println(a1.display());
     System.out.println(a2.display());
     System.out.println(b.display());
     System.out.println(c.display());
     System.out.println("a1.f(a2)   "+a1.f(a2));
     System.out.println("a1.f(b)   "+a1.f(b));
     System.out.println("a1.f(c)   "+a1.f(c));
     System.out.println("a1.f(d)   "+a1.f(d));
     System.out.println("a2.f(a1)   "+a2.f(a1));
     System.out.println("a2.f(b)   "+a2.f(b));
    System.out.println("a2.f(c)   "+a2.f(c));
    System.out.println("a2.f(d)   "+a2.f(d));
    System.out.println("b.f(a2)   "+b.f(a2));
     System.out.println("b.f(b)    "+b.f(b));
     System.out.println("b.f(c)    "+b.f(c));
     System.out.println("b.f(d)    "+b.f(d));
     }
    }
    /////////////////////////////////////////////////////////
    /////////////////////////////////
    ////////////////
    //////
    输出位:
    A的对象
    B的对象
    B的对象
    B的对象
    a1.f(a2)   A and A
    a1.f(b)   A and B
    a1.f(c)   A and B
    a1.f(d)   A and D
    a2.f(a1)   B and A
    a2.f(b)   B and B
    a2.f(c)   B and B
    a2.f(d)   A and D
    b.f(a2)   B and A
    b.f(b)    B and B
    b.f(c)    B and B
    b.f(d)    A and D
    ------------------------------------------
    因为a2是被声明为A的啊
    所以重载的时候把它看做A
    而动态绑定的时候才把它看做B
    所以调用a1.f(a2)时候把a2看成A的对象,自然调用A类中的public String f(A obj){return ("A and A");}方法
    类似b.f(a2)   B and A也可以解释通!
      

  24.   

    回复人:piaopiao11() 好象是方法调用的优先问题this.f(o)
    super.f(o)
    this.f((super)o)
    super.f((super)o);
    按照这个在看看
    super();对象可以使用父类的所有方法和变量。
    this();可以访问父类和子类中所有的方法或变量(父类中的构造方法除外)。这个回答就是最正确的了~你慢慢体会体会
    涉及到了"运行时类型识别".
    默认状态(和强制类型转换状态相区别)下,对象只能调用父类方法和父类变量。此种情况下,还有两种不同的情况:
    ▲被调用方法是被该子类覆盖的方法,这时其实是显式调用父类方法,实际上是调用该子类方法和该子类变量,这时父类变量不能被该方法修改,被修改的是该子类变量。(也就是后期绑定.)
    ▲被调用方法没有被该子类覆盖,这时是调用父类方法,父类变量可以被该方法修改。
      

  25.   

    搞清楚overload跟override之间的区别
      

  26.   

    我觉得应该是优先匹配声明类中的方法,如A a = new B(),应该是优先匹配A类中的方法。如果找不到在匹配类B中的方法,如果还没有再匹配其他父类中的方法。
      

  27.   

    参数匹配应该是优先匹配相同参数类,如果没有就匹配父类,最后匹配子类,如a1.f(b))中b优先匹配和自己相同的类B,没有!就匹配b类型的父类A以及其他祖先类,如果还没有则可能匹配其子类D以及其他后代类,
    不建议还是在调用时准确表明匹配哪个类的方法。想楼主那样的程序在实际应用中我实在不敢苟同。
      

  28.   

    通过以上各位的观点,我总结一下,并增加个人观点:
    class B extends A{
    public String f(B obj){return ("B and B");}
    public String f(A obj){return ("B and A");}
    }
    因为a2在静态联编时,a2是被认为是A类的,
    a2在匹配函数是从超类开始的,后再到子类,看是否有重写的函数,以实现多态;
    可以测试如下;
    class B extends A{
    public String f(B obj){return ("B and B");}
    //public String f(A obj){return ("B and A");}//屏蔽这一行
    }
    System.out.println(a2.f(b));// 结果为A and A;
    而b在静态联编时,就是个B类,
    b在匹配函数就是从本类开始,
    若未找到函数,就再上朔到超类,
    这样就不难理解了,
      

  29.   

    System.out.println(b.f(c));    B and B
    System.out.println(b.f(d));   A and DC和D都tmd是从直接B继承来的,怎么结果会不一样?
      

  30.   

    xiao7cn(烧鸡) ( ) 信誉:100    Blog  2006-11-17 14:17:34  得分: 0  
     
     
       System.out.println(b.f(c));    B and B
    System.out.println(b.f(d));   A and DC和D都tmd是从直接B继承来的,怎么结果会不一样?因为,B是A的子类,而A类实现了f(D ),而C则不同了,A和B类都实现f(c),所以C必须向上转形.不知道是不是说明白了.我的理解是这样的.  
     
      

  31.   

    看来好些人给弄晕了,
    大家犯迷糊的地方主要是a2.f(b)和a2.f(c)的输出。
    经过我的测试,
    A a2= new B();之后,a2的类型和属性问题我们要弄清楚,经过这个向上转型后,a2的类型是A类,a2里的属性方法有f(D obj)和f(A obj)这两个,f(A obj)是B类的那个方法,它覆盖了A类的f(A obj)。所以
    a2的方法是:{ public String f(D obj){return ("A and D");
                 public String f(A obj){return ("B and A");}
                 }
    所以,a2.f(b)和a2.f(c)的输出是:B and A,这里c向上转型到A类型。我已经说得够清楚了。
      

  32.   

    另外,我还测试了 wjj0532(西农小小生) 给出的程序,他这个程序跟楼主的差不多,但输出结果却不一样,主要原因是他这个程序的A类多了一个f(B obj)方法。所以,B类覆盖了A类的f(B obj)和f(A obj);a2的方法变成了三个:
    { public String f(D obj){return ("A and D");
     public String f(B obj){return ("B and B");
    public String f(A obj){return ("B and A");
                 }
    因此,a2.f(b)和a2.f(c)首先匹配f(B obj),导致输出结果是B and B.
      

  33.   

    这是个类型转型导致方法丢失的问题。
    A类没有B类的f(B obj)方法,当B向上转型时,对象a2会丢失B的f(B obj)方法。这里还涉及了方法覆盖问题,B类的f(A obj)方法覆盖了A类的f(A obj)。
      

  34.   

    a2对象是看不到B类的 public String f(B obj) 方法的,所以只能调用A类接口自己的方法了
      

  35.   

    其实楼主只要记住A a1中的a1是引用,就好像是C语言中的指针,在看看其他人说的重载和多态的关系,就好理解这些了
      

  36.   

    a2对象是看不到B类的 public String f(B obj) 方法的-------------------------------为什么看不到?new B()难道不会重载这个方法吗?
      

  37.   

    哦 不会重载 A类里没有这个f(B obj)。  我的意思是B类不会添加这个方法进来吗?
      

  38.   

    其实问题很简单
    我们来看看经过上面的类定义后,B类包含的方法:
    1)public String f(D obj){return ("A and D");}
    2)public String f(B obj){return ("B and B");}
    3)public String f(A obj){return ("B and A");}所以在调用System.out.println(b.f(d));时,自然会找到最匹配的方法1),输出结果也就是:A and D了!
      

  39.   

    前三个结果很容易理解,A类对象的引用a1指向了A类的对象,所以,它可以引用的方法也就只有两个,即
     public String f(D obj){return ("A and D");}
     public String f(A obj){return ("A and A");}
    就是A类中的方法,所以在调用时只要传进来的对象不是D类的对象就一律返回"A and A"(因为A类是这里的根类),要不就返回"A and D"。
        接下类的三个就比较有意思,a2是一个指向子类对象的引用,也就是说用它调用方法时只能调用
     public String f(D obj){return ("A and D");}
     public String f(A obj){return ("A and A");}
      为什么他可以调用子类中的方法呢?原因就在于他其实没有调用子类中的方法,只是因为子类中的一个方法把它的一个方法给覆盖掉了(A类的第二个方法和B类的第二个方法同名且同参),所以,当父类引用调用public String f(A obj)时,实际执行的却是子类中的相同的方法,即相当于他能调用的方法变成了
    public String f(D obj){return ("A and D");}
    public String f(A obj){return ("B and A");}
      所以在传进去的参数不是D类的对象时都会返回"B and A"。是D类的对象则返回“A and D”
     
      最后的三个更容易理解,B类引用b指向的是B类对象,所以他就可以调用按理说是A类和B类的共计4个方法,但是因为用两个是同名又同参的所以就只有三个,即:
     public String f(B obj){return ("B and B");}
     public String f(A obj){return ("B and A");}
     public String f(D obj){return ("A and D");}
      所以会有最后三个的输出!!!
      

  40.   

    呵呵,这个答复,我也见过,不过还是要说谢谢!
    我又对这个问题做了一下总结,如下:这几个类到最后变成这样的了:
    class A{
      public String f(D obj){return ("A and D");}
      public String f(A obj){return ("A and A");}
    }
    class B extends A{
      public String f(D obj){return ("A and D");}
      public String f(B obj){return ("B and B");}
      public String f(A obj){return ("B and A");}
    }
    class C extends B{
      public String f(D obj){return ("A and D");}
      public String f(B obj){return ("B and B");}
      public String f(A obj){return ("B and A");}
    }
    class D extends B{
      public String f(D obj){return ("A and D");}
      public String f(B obj){return ("B and B");}
      public String f(A obj){return ("B and A");}
    }
    class test{
         public static void main(String[] ss)
         {
           A a1 = new A();
           A a2 = new B();
           B b = new B();
           C c = new C();
           D d = new D();
           System.out.println(a1.f(b));  
          //A and A 调用含b的方法,没有找到,就找到含它的父类A的方法,
          //即public String f(A obj){return ("A and A");}
          ----------------------------------------------
           System.out.println(a1.f(c));   //A and A 同上
         ----------------------------------------------
           System.out.println(a1.f(d));  // A and D 这个就不用说了吧
         ----------------------------------------------
           System.out.println(a2.f(b));  
         /* B and A 这里,用父类的引用指向子类的对象,但父类的引用只能调用子类中和父类完全一致的方法,
         也就是说,这里可以用的方法只有这两个:
           public String f(D obj){return ("A and D");}
        public String f(A obj){return ("B and A");}
      也就是说出现了和第一个一样的情况了,要找它的父类的那个f(A obj);
         ----------------------------------------------
           System.out.println(a2.f(c));  //  B and A 同上了
           System.out.println(a2.f(d));  // A and D
         -----------------------------------------------
           System.out.println(b.f(b));   // B and B
           System.out.println(b.f(c));   // B and B这个因为它的直接父类的是f(B obj)
           System.out.println(b.f(d));  // A and D
         }
    }
      

  41.   

    private Method findVisitMethod(Class rootArgumentType) {
    if (rootArgumentType == Object.class) {
    return null;
    }
    LinkedList classQueue = new LinkedList();
    classQueue.addFirst(rootArgumentType); while (!classQueue.isEmpty()) {
    Class argumentType = (Class)classQueue.removeLast();
    // Check for a visit method on the visitor class matching this
    // argument type.
    try {
    if (logger.isDebugEnabled()) {
    logger.debug("Looking for method " + VISIT_METHOD + "(" + argumentType + ")");
    }
    return findVisitMethod(this.visitorClass, argumentType);
    }
    catch (NoSuchMethodException e) {
    // Queue up the argument super class if it's not of type Object.
    if (!argumentType.isInterface() && (argumentType.getSuperclass() != Object.class)) {
    classQueue.addFirst(argumentType.getSuperclass());
    } // Queue up argument's implemented interfaces.
    Class[] interfaces = argumentType.getInterfaces();
    for (int i = 0; i < interfaces.length; i++) {
    classQueue.addFirst(interfaces[i]);
    }
    }
    }
    // No specific method found -> return the default.
    return findDefaultVisitMethod();
    } private Method findVisitMethod(Class visitorClass, Class argumentType) throws NoSuchMethodException {
    try {
    return visitorClass.getDeclaredMethod(VISIT_METHOD, new Class[] {argumentType});
    }
    catch (NoSuchMethodException ex) {
    // Try visitorClass superclasses.
    if (visitorClass.getSuperclass() != Object.class) {
    return findVisitMethod(visitorClass.getSuperclass(), argumentType);
    }
    else {
    throw ex;
    }
    }
    }spring中的代码,有助于大家理解。
      

  42.   

    对Java的底层不熟,不如看看C++的吧,清楚明了:
    using namespace std;class D;class A
    {
    public:
    virtual string f(D* obj){return string("A and D");}
    virtual string f(A* obj){return string("A and A");}
    };
    class B : public A
    {
    public:
    virtual string f(B* obj){return string("B and B");}
    virtual string f(A* obj){return string("B and A");}
    };
    class C : public B
    {};
    class D : public B
    {};int main(int argc, char* argv[])
    {
    A* a1 = new A;
    A* a2 = new B;
    B* b = new B;
    C* c = new C;
    D* d = new D; cout << a1->f(b) << endl;    //A and A
    cout << a1->f(c) << endl;    //A and A
    cout << a1->f(d) << endl;    //A and D
    cout << a2->f(b) << endl;    //B and A
    cout << a2->f(c) << endl;    //B and A
    cout << a2->f(d) << endl;    //A and D
    cout << b->f(b) << endl;    //B and B
    cout << b->f(c) << endl;    //B and B
    cout << b->f(d) << endl;    //B and B,此处与java的结果不同?
    return 0;
    }
    在C++中,多态由指针或引用加上虚函数来实现,每个含有虚函数的类有一个或几个虚函数表,每个这样的类的对象有一个或几个指向虚函数表的指针,为什么说有一个或几个呢,主要是继承的原因吧。为什么会有上面的结果呢,主要因为对象(指针)的类型和首先调用哪个(继承来的父类的虚函数表,其中有些已被改写,还是自己虚函数表)虚函数表里的函数有关吧。不知道java底层如何实现的,应该也差不多吧?至于最后那个结果怎么不一样了,我不知道!!!!
      

  43.   

    看到我头都晕了,大家把问题想复杂了。不过他这里搞来搞去的大家容易想多,如果父类只有一个方法,而子类也只有一个方法,且此方法重写了父类方法,那么问题就简单多了。现在我们就照着这个思路来:
    只讲解 System.out.println(a2.f(b))其他的都一样了。
    首先我们看a2是声明为A的,那么当调用一个方法时,首先他会去查看A中有没有这个方法,现在看到有符合的,即public String f(A obj){return ("A and A");},但是慢着,我们说的多态的意思就是编译器会根据对象的实际型别去调用相应的方法,那么他又发现这个对象的实际型别是B,那么他就去看B类中哪个方法重写了public String f(A obj){return ("A and A");},很明显是public String f(A obj){return ("B and A");},那么输出B and A就理所当然了。个人觉得这么理解简单一点,前面有人讲到方法的调用顺序,我稍微看了一下,没看懂。
      

  44.   

    最后一个结果有点意思。这实际上就是解释的顺序问题,跟实现的关系很大。
    C++在调用虚函数时,总是先根据对象的实际类型来寻找。这是标准保证的,也非常适合于Lippman所讲的那种实现方式。
    而对Java来说,按照JLS15.12.2节里说的,它会先把可用的函数都列出来,然后再选择。这样当然就会选择到类A的那个成员,因为它的参数列表与传入的参数完全吻合。
      

  45.   

    上转型的对象将失去子类新增的字段(成员变量),和方法.而只能操作继承来的字段和方法以及被子类重写的方法.
    a2被向上转型成了A类,所以a2只能调用b.f(A obj),不能调用b.f(B obj)。
      

  46.   

    到后面讨论问题基本上讲清了,我就不多哆嗦了.我只解释一下xiao7c()的疑惑及一些Java中的基本概念,B类的对象当然拥有B类的任何方法,也拥有从父类那里继承过来的任何方法,成员变量也一样;对于重载,子类会覆盖的父类的同名方法,即子类中不存在这父类的方法,而成员变量则会隐藏,即子类中有两个同名的成员变量,从父类那里继承过来的那个被隐藏了,一般使用的是自己的变量,但你可以用super关键字来访问这个隐藏的变量.
        现在来看A a1=new B();我只解释一下设计的动机,a1是一个引用(上面有的人看到了),它是要指向A类的对象实体的,现在它指向了B类的对象实体了,但a1的样子依然是A类,它当然只会按照A类的行为及样子来访问B类的方法,这样你就知道为什么a1不能访问B类中添加的新方法和新成员变量,a1只能访问B类中从A类那里继承或重写的方法,注意并不是访问A类的(上面的很多人都错了),因为对象实体是由B类创建的(实体中有B类的任何东西).
        注意:可以将a1强制转换成B类的对象如(B)a1,这时a1就可以访问B类的所有属性和功能了.
        所以说:同志们哪,基础重要啊!!!
      

  47.   

    所以说,同志们哪,要了解Java语言规范及Java的编译机制呀,这样你才能真正的精通一门语言,而不是天天跟着这几个大厂商转个不停,这门语言也学,那门语言也学,什么都知道就等于什么都不精通!!!
      

  48.   

    顺便赞一下几个讲得比较好的:areshank(),heh_621(),lingwen20(),cattong(),xicheng_my_love(),qi37927(),分数应该给他们
      

  49.   

    这道题目如果谁熟悉模式,熟悉"李氏代换原则"就好理解了,所有父类使用的地方都可以用其子类代替,而父类不能代换子类;A和B使用的地方可以用子类C或D代换,而D不能用C来此换!
      

  50.   


    areshank(灰烬的苍鬼) :3)、B中的方法public String f(B obj){return ("B and B");}不存在于A类中,所以a2不会调用这个方法,不会输出"B and B"一句话点醒梦中人啊!谢了!总结一下,一句话:对于多态(例如a2)可以肆无忌惮的当作子类使用(例如b),除了一个区别:a2只能使用父类接口的方法,b则可以使用自己新定义的方法。
      

  51.   

    我认为“里氏代换原则”只是说了子类对象和父类对象直接是否能替换使用问题,并不能很好的解释这里出现疑惑的地方。这种疑惑也很有代表性,要理解这个问题关键还是将A a2 = new B();分成两部分来看,
    1。A a2 (有疑惑的人都是没有透彻的理解到它的意思)
    它表示在内存栈中声明一个A类型的对象a2,所以它就“规定”了a2对象只具有class A中定义的两种签名格式的方法。而由于参数b的类型B是A的子类,所以 a2.f(b) 只能匹配到最相近的方法签名 public String f(A obj) ;
    可以如下注释掉几行代码,编译看看结果(编译报错,想想为什么???)。
    package myExercise.oop;class A{
     public String f(D obj){return ("A and D");}
     //public String f(A obj){return ("A and A");}
    }
    class B extends A{
     public String f(B obj){return ("B and B");}
     public String f(A obj){return ("B and A");}
    }
    class C extends B{}
    class D extends B{}public class TestOverride{
    public static void main(String[] args)
    {
    A a1 = new A();
    A a2 = new B();
    B b = new B();
    C c = new C();
    D d = new D();
    //System.out.println(a1.f(b)); //A and A
    //System.out.println(a1.f(c)); //A and A
    //System.out.println(a1.f(d)); //A and D
    System.out.println(a2.f(b)); //B and A
    System.out.println(a2.f(c)); //B and A
    System.out.println(a2.f(d)); //A and D
    System.out.println(b.f(b)); //B and B
    System.out.println(b.f(c)); //B and B
    System.out.println(b.f(d)); //A and D
     }
    }2。= new B();
    它表示在内存堆中创建一个B类型的对象,并让a2这个引用指向这个堆中对象的内存地址。由于B中重写了public String f(A obj)方法,所以接下来发生的多态方法调用就很简单了,我相信这一步也难不到大家,关键还是理解第一步的意思。
      

  52.   

    sorry, 应该是 “它表示在内存栈中声明一个A类型的对象引用a2”
      

  53.   

    首先你要知道A a2 = new B()意味着什么?
    其次你要知道A{ f(A) f(B) }情况下,a.f(o)如何根据o的类型及f参数的类型进行推导以选择最适当的一个。在Language Spec都有明确规定的,自己查一下就明白了。
      

  54.   

    好象是方法调用的优先问题this.f(o)
    super.f(o)
    this.f((super)o)
    super.f((super)o);
    按照这个在看看===
    一句中地,C++版也出现同样的题目
      

  55.   

    这种类型的题目以后会经常遇到,其实如果你对JAVA理解深的话很容易解决.首先你要知道在JAVA中所有对象变量都是引用,只不过因为JAVA中没有指针的概念,而且JAVA中的指针是不可计算的(不能a1++),所以在一般的教材中不引入引用和指针的概念(因为没有实际的应用价值,讨论起来只会越来越复杂),所以我们常说B b=new B();是创建了一个B类对象b,实际上是创建了一个B类引用,用它指向了一个B类对象实体.在JAVA只有new关键字才能真正地创建一个对象实体,其他的都不能;那么A a1=new B()就是创建了一个A类引用,用它指向了一个B类对象实体,这在JAVA中是合法的,为什么???B是A的子类,B类对象总是可以当作A类对象来使用的,我是学生,学生是人,所以我是人(这应该懂吧).
        在JAVA中,对象变量调用方法调用的肯定是对象实体中的方法(这是理所当然的)而且是与引用类型中一样的方法,知道这一点你就可以解决任何类似的问题.首先你要分析引用是什么类型,那么它就只能调用与类型中一样的方法(因为引用只记住了该类型的样子),然后你要分析对象实体中有什么方法(这非常关键,特别要弄清继承关系),把类型中方法和实体中的方法对照一下,就可以知道a1.f()调用的到底是哪一个方法.
      

  56.   

    顶下,慢慢看各位老师的内容------ 以下是签名 ------
    SoftDN.com, 提供免费的项目管理平台:SVN仓库、需求管理、BUG跟踪、任务管理、在线论坛、网站空间、项目存储及完整的备份等。欢迎网友光临建立项目,开创自己的梦想。
    地址:http://www.softdn.com   http://www.softdn.cn
      

  57.   

    看到我头都晕了,大家把问题想复杂了。不过他这里搞来搞去的大家容易想多,如果父类只有一个方法,而子类也只有一个方法,且此方法重写了父类方法,那么问题就简单多了。现在我们就照着这个思路来:
    只讲解 System.out.println(a2.f(b))其他的都一样了。
    首先我们看a2是声明为A的,那么当调用一个方法时,首先他会去查看A中有没有这个方法,现在看到有符合的,即public String f(A obj){return ("A and A");},但是慢着,我们说的多态的意思就是编译器会根据对象的实际型别去调用相应的方法,那么他又发现这个对象的实际型别是B,那么他就去看B类中哪个方法重写了public String f(A obj){return ("A and A");},很明显是public String f(A obj){return ("B and A");},那么输出B and A就理所当然了。个人觉得这么理解简单一点,前面有人讲到方法的调用顺序,我稍微看了一下,没看懂。====================================================
    这位大侠说的好,就是这么简单的问题,讨论的人多了,反而把问题搞复杂了
      

  58.   

    大家可以结合分析下面代码段到底做了什么来理解我讲的东西:
    A a,b;
    a=new A();
    b=a;
    分析:创建了两个引用变量a,b(我们常说的是声明了两个对象a,b)-->创建一个A类对象实体,用引用a指向它(我们常说的是创建对象a)-->把引用a赋给引用b,也就是使b中存储的值与a中的值相等,这样b也指向了A类的对象实体(我们常说的是把对象a赋给对象b).
        对于a.f(),其中的点号是一个运算符,它的功能就是访问a所指向的对象实体中的方法f(),你可以把它理解成一个二元运算符,左边是一个指向某对象实体的引用变量,右边是该对象实体中的f()方法的入口地址.
      

  59.   

    我扯得都是JAVA中的一些基本概念且是比较深的部分,一般书籍中都是不会介绍的,希望对大家深入学习JAVA语言有所帮助,如果你只是想理解本贴中的程序的话,只要看我赞扬的那几个人写的就足够了,其他的只会越看越迷惑
      

  60.   

    刚开始不明白,看了整个帖子,终于豁然开朗了其实很多人没讲清楚,这个问题,首先明确静态编译和动态绑定2个完全隔离的阶段,就好理解了。在静态编译阶段,a2.f(b) 因为a2是A类型的引用,所以被视为 A.f(b) ,更进一步被视为 A.f( A)。因此在产生的字节码里,此处生成的是A.f(A)的方法调用。(这里决定了一切)接下来到了运行时,动态绑定,因为a2的运行时类型实际上是B,所以这时A.f(A)实际上调用了其子类B.f(A)方法。很多人不明白为何没有调用B.f(B),实在是因为没有严格把两个阶段分割开来。再次感谢各位高手的教导
      

  61.   

    楼主的帖子在这里值得我们关注的东西有三点,这里以
     System.out.println(a2.f(b));   B and A
     System.out.println(a2.f(c));   B and A
    来说明问题,其他比较简单就不详细阐述。
    1、调用哪个类。
       在这里很明显a2调用的是A的引用,B类的实例。这里有个规则:引用的类型和实例的类型不一样时,如果出现方法名和字段名相同时,方法调用引用类型的,字段的值是实例类型的。
    2、调用类中的哪个接口,这个接口包括方法名和参数,不包括里面具体的实现。
       由于a2调用的是A的引用,所以我们只能调用A的接口,在这里A的接口只有两个,即
       public String f(D obj)
       public String f(A obj)
       看上面的输出语句中的参数,b,c都是A类的子类,可以转换为A类的类型,而b,c和D类不存在这种关系,在这里只能选择A中的f(A obj)方法。    
    3、调用接口的具体实现即我们应该调用引用类型和实例类型中哪个方法中的代码。
       这就是方法覆盖的问题,由于a2的引用接口调用的是B类的实例,即B类的代码是在内存中的具体表现,A类的代码没有在内存中实例化,所以A.f(A obj)接口调用的代码是B.f(A obj){return ("B and A");}的代码。