先看这个程序:
import java.util.*;
import static net.mindview.util.Print.*;class Processor{
public String name(){
return getClass().getSimpleName()+f();
}
Object process(Object input){
return input;
}
public String f(){
                return "父类的f()方法";
}
}class Upcase extends Processor{
public String f(){
return "子类的f()方法";
}
String process(Object input){
return ((String)input).toUpperCase();
}
}public class Apply {
    public static void process(Processor p,Object s){
     println("Using Processor"+p.name());
     println(p.process(s));
    }
    public static String s = "Disagreement with beliefs is by definition incorrect";

public static void main(String[] args) {
            process(new Upcase(),s);
}}
程序结果为:
Using ProcessorUpcase子类的f()方法
DISAGREEMENT WITH BELIEFS IS BY DEFINITION INCORRECT请问:
public static void process(Processor p,Object s)方法中的 p.name() 调用的f()方法为什么是子类的而不是父类的呢 它的真实原因是什么呢 请尽量详细 请不要用多态一笔带过
谢谢

解决方案 »

  1.   

     process(new Upcase(),s); 
    这样你实际上调用process方法的是Upcase对象,
    相当于Processor p = new new Upcase();
         p.name();
      

  2.   


    Processor p = new Upcase(); 
        p.name(); 
      

  3.   

    关于多态我才看了点书,说来大家看看对不对:
    一个父类Father有方法A,B,子类Child继承后也有方法A,B,并且改写了A,B。并且自己还有个方法C,
    这个时候如果用父类的引用p指向子类的对象即:
    Father p = new Child();那么可以理解为p只能看到新new 出来的Child的对象的内存中继承于父类的部分。
    也就是说p可以看到内存中A,B,但是看不到C。重点是p看到的A,B是方法的引用,而这个引用指向内存中被子类改写过的方法的地址。
    所以调用p.A()的时候,是调用的子类的方法。找着这个分析内存应该可以解决吧,你没写注释,我还要赶作业,没仔细看,对不起
      

  4.   

    这就是多态啊,
    编译时类型 != 运行时类型
    这时发生动态绑定,运行时环境查找到对象的真实类型做调用。这个真实类型的检查跟RTTI(runtime type information)有关,简单来说就是类加载器加载类型(Class类)
      

  5.   

    向上转型,如果不说多态的话,那就看3楼的解释吧,比较明确,用接口来理解起来更好一些
    比如说父类是个接口A,有一个方法subShow(),
    类B,C都实现了接口A,
    然后分别A b = new B(); A c = new C();
    再调b.subShow()和c.subShow()就明确很多了java编程思想里对这方面有比较详细的论述~
      

  6.   

    都构成覆盖了,process中new的是子类就像实例化接口嘛,实现是由子类完成的
      

  7.   

    这个是一个典型的多态,这里运用了动态绑定也有的叫晚绑定。class Processor {
    public void a() {
    System.out.println("Processor.a()");
    } public void b() {
    System.out.println("Processor.b()");
    }
    }
    class Upcase extends Processor { public void b() {
    System.out.println("Upcase.b()");
    } public void c() {
    System.out.println("Upcase.c()");
    }
    }
    class Apply { public static void main(String[] args) {
    Processor pro = new Upcase();
    pro.a();//Processor.a()              位置1
    pro.b();//Upcase.b()                 位置2  
    //pro.c();//如果把注释去掉,编译错误     位置3 }
    }修改了LZ的例子,只是为了更容易说明问题,
    我们要关注的问题并没有改变,这里就不做太多说明。Processor pro = new Upcase();
    在jvm载入类的时候同时也记录了该类的方法,可以理解为同时保存了一个方法表。
    因为pro是Processor 类型的,所以方法表中记录了Processor 的方法。
    所以我们就可以解释 位置1 和 位置3 
    而实际pro却是一个Upcase的实例,所以在执行到 位置2 的时候 运用了晚绑定 执行的就是 Upcase的方法。结论:
    父类 实例 = new 子类();
    这样造型的时候 实例可以调用父类中所有特有的方法(未在子类中覆盖的方法)
    实例可以调用子类中覆盖父类的方法,但不可以调用子类中特有的方法(即父类中没有的方法)。
     
      

  8.   

    按3楼的理解 我还有一点不明白: 如果基类Processor是抽象类 而在子类中使用super.f()调用抽象基类的被覆盖方法f()时 该怎么调用呢 怎么能调用呢 因为这个子类对象开辟的内存中根本没有这个抽象基类的这个被覆盖的方法啊 但却又不报错 难道隐式的创建基类对象吗 但问题是抽象基类根本不能创建对象啊 
    或者说创建子类对象的时候子类对象包含了基类和子类的所有方法 也为基类的被覆盖方法 和 子类的覆盖方法都分配了内存空间吗 但这样理解 我又很迷糊 请高手帮帮忙指教 谢谢
      

  9.   

    Processor p = new Upcase(); 
    对象指向Upcase
    调用Upcase方法!
      

  10.   


    package com.chen.Application;abstract class Processor {
        private int i = 1;    
        public void a() {
            System.out.println("父亲的.a()"+"\t i = "+i);
        }    public void b() {
            System.out.println("父亲的.b()");
        }
        
        public void setI(int i)
        {
         this.i = i;
         System.out.println("\t i = "+i);
        }
    }
    class Upcase extends Processor {    public void b() {
    // System.out.println("孩子.b()");
         super.b();

        }    public void c() {
            System.out.println("孩子.c()");
        }
    }
    public class Apply {    public static void main(String[] args) {
            Processor pro = new Upcase();
            pro.a();//Processor.a()              位置1
            pro.b();//Upcase.b()                 位置2  
            pro.setI(5);        ((Upcase)pro).c();//如果把注释去掉,编译错误     位置3    }
    }我想问一下,我把父类声明为抽象类,
    既然Processor pro = new Upcase();
    说明是通过子类Upcase来分配内存空间的,
    为什么在子类super.b()方法能调用到父类的数据,
    而通过pro.setI(5);方法也能修改父类的数据,
    抽象类Processor虽然不能用new来分配内存空间,是不是java虚拟机在为new Upcase()分配内存空间的时候,
    内部也隐式为Processor类分配了内存空间啊,
    如果不是的话,子类创建的对象为什么能调用和修改父类的数据呢?
      

  11.   


    package com.chen.Application;abstract class Processor {
        private int i = 1;  
        public void a() {
            System.out.println("父亲的.a()"+"\t i = "+i);
        }    public void b() {
            System.out.println("父亲的.b()");
        }
        
        public void setI(int i)
        {
         this.i = i;
         System.out.println("\t i = "+i);
        }}
    class Upcase extends Processor {    public void b() {
            // System.out.println("孩子.b()");
         super.b();
        }    public void c() {
            System.out.println("孩子.c()");
        }
    }
    public class Apply {    public static void main(String[] args) {
            Processor pro = new Upcase();
            pro.a();//Processor.a()              位置1
            pro.b();//Upcase.b()                 位置2  
            pro.setI(5);        
            ((Upcase)pro).c();//如果把注释去掉,编译错误     位置3
        }
    }我想问一下,我把父类声明为抽象类,
    既然Processor pro = new Upcase();
    说明是通过子类Upcase来分配内存空间的,
    为什么在子类super.b()方法能调用到父类的数据,
    而通过pro.setI(5);方法也能修改父类的数据,
    抽象类Processor虽然不能用new来分配内存空间,是不是java虚拟机在为new Upcase()分配内存空间的时候,
    内部也隐式为Processor类分配了内存空间啊,
    如果不是的话,子类创建的对象为什么能调用和修改父类的数据呢?
      

  12.   

    LZ可以看看《java编程思想》第四版的 第8章 多态;这就是前期绑定和动态绑定的问题,java中static方法, private方法, final方法是前期绑定
    其他方法是动态绑定的。
      

  13.   

    为了解决你的这个问题,我翻了翻有关jvm的书。终于找到了答案。然而详细的细节说出来可能太长了,我捡重要的说一下,尽量详实。因为方法的特殊性,首先是将子类的方法生成,然后去执行构造函数。(这里很多朋友有迷惑,认为构造方法是在初始化块或者初始化变量之后进行的,其实不是这样的。初始化块可以看作构造方法第一行程序代码,总是被最先执行的部分。在执行构造方法时,先执行了初始化块,然后再执行构造方法里面的其他程序代码,要把初始化块这些东西看作构造方法的一部分,就好理解了。这些都是很表层的东西,你可以简单的利用断点看出来,程序会先找到构造函数,然后再去初始化块,在然后执行构造方法内的其他程序代码。)
    注意上面说的是没有父类的类(非衍生类)的构造方法。衍生类构造方法的执行顺序是:1、自己的构造方法 2、(在这之前先生成父类所有方法的实例***)父类的构造方法 3、父类的初始化块 4父类的构造方法中的其余代码 5、子类的初始化块 6、子类构造函数中的其他代码。
    我们来看***部分的具体执行,在生成第N级类的方法时,会拿第N级类的方法和第N+1级类的方法比较,有相同的话,将其丢到一个栈中,并将第N级类的方法指向第N+1级类的方法的引用。也就是说我们在第N级类中调用签名与第N+1级类的方法签名相同的方法时,将实际调用第N+1级类的方法。又因为相同的方法可能很多,所以将这些相同的方法,也就是被覆盖的方法打成集合丢进栈中。这里注意即使第N级类的方法是一个抽象方法,仍然会将其放入栈中,但是当读者想要用super.调用这个抽象方法时,编译器会报错。我们得到的这个覆盖方法栈(后面只用一个‘栈’字代替)可以被super.调用,具体后面再说。
    当生成最上层的祖父类时,我们已经得到了一个堆满被覆盖的方法的栈。祖父类开始构造第一个对象,这才是构造衍生类对象的第一步。祖父类的方法如果被覆盖了,则在这里调用相同签名的方法时,调用的为下一级的方法,如果下一级的方法也被覆盖了,则调用更下一级的方法。要注意,虽然祖父类下一级的类的对象没有生成,但是方法已经都准备好了,所以可以做这样的检查。然后顺利的生成祖父类的对象,这个对象就如大家想象的那样,被包含在衍生类里。然后生成下一级的类。在生成下一级的类时,我们如果需要调用祖父类的方法的话,可以使用super.+方法名去访问‘栈’顶的方法。注意当生成了第二级类的对象后,进入生成第三级类时,我们的‘栈’将脱掉自己的顶。这时你要访问上一层的方法,一样用super.+方法名的形式的访问,但是你无法访问更上一层的方法。因为已经被丢弃了。丢弃到哪里了呢?不是说衍生类的对象中包含着这些方法么?没错就是丢弃到子类对象中去了。通过在子类对象中建立一个新栈来一次一次接收从‘栈’中丢弃的方法。(但严格地说,因为资源问题,jvm在实际处理时采用了特殊机制,而不是每个对象中都有这些方法的实例。理解就好。)这些被覆盖的方法,子类的对象可以调用么,一般来说是不行的。
    总的来说,基本上就是这样,如果要理解的话,第一、要清楚Method类是什么东西,第二、,的编我是都全容内的面上。关键是第二点,如果看不懂的话,可以倒过来念。
      

  14.   

    根据大家的回答,大体上明白了一些《Java语言程序设计》(电子工业出版社出版)76页那个程序的运行结果。
    大家说的很好,很收益。