先看这个程序:
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()方法为什么是子类的而不是父类的呢 它的真实原因是什么呢 请尽量详细 请不要用多态一笔带过
谢谢
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()方法为什么是子类的而不是父类的呢 它的真实原因是什么呢 请尽量详细 请不要用多态一笔带过
谢谢
这样你实际上调用process方法的是Upcase对象,
相当于Processor p = new new Upcase();
p.name();
Processor p = new Upcase();
p.name();
一个父类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()的时候,是调用的子类的方法。找着这个分析内存应该可以解决吧,你没写注释,我还要赶作业,没仔细看,对不起
编译时类型 != 运行时类型
这时发生动态绑定,运行时环境查找到对象的真实类型做调用。这个真实类型的检查跟RTTI(runtime type information)有关,简单来说就是类加载器加载类型(Class类)
比如说父类是个接口A,有一个方法subShow(),
类B,C都实现了接口A,
然后分别A b = new B(); A c = new C();
再调b.subShow()和c.subShow()就明确很多了java编程思想里对这方面有比较详细的论述~
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 子类();
这样造型的时候 实例可以调用父类中所有特有的方法(未在子类中覆盖的方法)
实例可以调用子类中覆盖父类的方法,但不可以调用子类中特有的方法(即父类中没有的方法)。
或者说创建子类对象的时候子类对象包含了基类和子类的所有方法 也为基类的被覆盖方法 和 子类的覆盖方法都分配了内存空间吗 但这样理解 我又很迷糊 请高手帮帮忙指教 谢谢
对象指向Upcase
调用Upcase方法!
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类分配了内存空间啊,
如果不是的话,子类创建的对象为什么能调用和修改父类的数据呢?
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类分配了内存空间啊,
如果不是的话,子类创建的对象为什么能调用和修改父类的数据呢?
其他方法是动态绑定的。
注意上面说的是没有父类的类(非衍生类)的构造方法。衍生类构造方法的执行顺序是: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类是什么东西,第二、,的编我是都全容内的面上。关键是第二点,如果看不懂的话,可以倒过来念。
大家说的很好,很收益。