[讨论] 关于继承关系中的方法分派的疑惑
chenjingbo
2011-08-09
这个疑问来自iteye论坛中的某个帖子.具体的帖子地址忘记记了,就算单独的问题吧.问题如下
public class SuperTest { public static void main(String[] args) { new Sub().exampleMethod(); } } class Super { private void interestingMethod() { System.out.println("Super's interestingMethod"); } void exampleMethod() { interestingMethod(); } } class Sub extends Super { void interestingMethod() { System.out.println("Sub's interestingMethod"); } }
这个输出是
Super's interestingMethod 也就是说 ,它调用了父类的私有方法(这个时候其实不算继承). 我们修改一下代码
public class SuperTest { public static void main(String[] args) { new Sub().exampleMethod(); } } class Super { void interestingMethod() { System.out.println("Super's interestingMethod"); } void exampleMethod() { interestingMethod(); } } class Sub extends Super { void interestingMethod() { System.out.println("Sub's interestingMethod"); } }
其他并没有任何改变,就是把父类中的方法修饰改成了默认.对应的输出为 Sub's interestingMethod
我想问的是,在exampleMethod方法体中,它的如何找到对应子类的方法实现的呢.是在编译器确定的吗?还是运行期. 求解 |
|
RednaxelaFX
2011-08-09
我觉得这帖里的内容能回答你的问题:http://rednaxelafx.iteye.com/blog/652719
关键点在于“Java里什么是虚方法”以及“虚方法如何分派”。 Java里只有非private的成员方法是虚方法。 所以你会留意到在顶楼例子的第一个版本里,exampleMethod()是用invokespecial来调用interestingMethod()的;而第二个版本里则是用invokevirtual。 更详细的说明留给楼下 |
|
chenjingbo
2011-08-10
RednaxelaFX 写道 我觉得这帖里的内容能回答你的问题:http://rednaxelafx.iteye.com/blog/652719
关键点在于“Java里什么是虚方法”以及“虚方法如何分派”。 Java里只有非private的成员方法是虚方法。 所以你会留意到在顶楼例子的第一个版本里,exampleMethod()是用invokespecial来调用interestingMethod()的;而第二个版本里则是用invokevirtual。 更详细的说明留给楼下 我看了下上面你的这个帖子,你是说 "除了静态方法之外,声明为final或者private的实例方法也是非虚方法。其它实例方法都是虚方法。 " 是这样吧. |
|
RednaxelaFX
2011-08-10
chenjingbo 写道 我看了下上面你的这个帖子,你是说
"除了静态方法之外,声明为final或者private的实例方法也是非虚方法。其它实例方法都是虚方法。 " 是这样吧. 对。不过Java语言里final方法的有趣点是:final但非private的实例方法还是用invokevirtual去调用的。虽然行为不是虚的。有别的原因导致这个设计。 |
|
chenjingbo
2011-08-10
如何知道 exampleMethod() 是通过 什么指令调用 interestingMethod() 的呢.
我通过javap 只能看到 exampleMethod()方法的 引用 public class SuperTest {
public SuperTest(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>": ()V 4: return public static void main(java.lang.String[]); Code: 0: new #2 // class Sub 3: dup 4: invokespecial #3 // Method Sub."<init>":()V 7: invokevirtual #4 // Method Sub.exampleMethod:()V 10: return } |
|
RednaxelaFX
2011-08-10
哈哈,你用javap的时候得指定“类名”,跟源码的“文件名”没有关系。
既然你要观察的东西是Super.exampleMethod()的内容,那就得: javap -verbose Super |
|
chenjingbo
2011-08-10
看到了.哈哈.
我是否可以这么理解.如果是 "invokespecial" 指令的,都是静态分派的.只要看常量池中对应哪个方法就会执行那个方法.而 "invokevirtual"指令的,都是需要根据 目标对象的具体类型来动态判断执行哪个方法呢? |
|
RednaxelaFX
2011-08-10
chenjingbo 写道 我是否可以这么理解.如果是 "invokespecial" 指令的,都是静态分派的.只要看常量池中对应哪个方法就会执行那个方法.而 "invokevirtual"指令的,都是需要根据目标对象的具体类型来动态判断执行哪个方法呢?
我可能不会用这样的词。我会说invokevirtual的语义是要尝试做虚方法分派,而invokespecial不尝试做虚方法分派。 主要是因为我不喜欢“静态分派”这种叫法…嗯这是我的个人喜好问题。 我还以为我挖了坑的话会被问…final跟invokevirtual的关系。嘛没问题就好 ^_^ |
|
chenjingbo
2011-08-10
刚才一直在纠结上面的方法分派问题..所以没去关注你说的final方法的特殊点哦.很明显,我不清楚..
求解啊.撒迦 |
|
NightWatch
2011-08-11
静态分派根据引用类型来确定方法,这里调用到了父类的interestingMethod。
但是声明引用的时候是: Subclass me = new Subclass(); me.exampleMethod(); 引用类型应该是Subclass才对啊,是不是因为调用到 exampleMethod方法的时候内部的this引用类型变成了父类Super类型而不再是子类 Subclass类型呢? |