[讨论] 关于继承关系中的方法分派的疑惑

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类型呢?
Global site tag (gtag.js) - Google Analytics