考虑以下类层次结构:
class A {
compute(int a) {
compute(a, 1);
}
compute(int a, int b) {
// do some things
}
}
class B extends A {
compute(int a, int b) {
// do some stuff
}
}
如果我使用A引用初始化B对象,如下所示:
A foo = new B();
如果我打电话:
foo.compute(1)
然后,在此方法调用中,它会调用A类compute(int, int)
或致电B类compute(int, int)
?
来自官方java文档的任何引用?
答案 0 :(得分:0)
我猜你是一名C ++程序员。当您未将compute(int, int)
函数定义为virtual
时,您指向的行为确实会在C ++中发生。
C ++设计人员总是在寻找极致性能,因此在编译时定义对象的非virtual
方法调用,以避免在vtable
中对函数指针进行额外引用,也就是它拥有的虚拟功能表。
如果是这样的话,让我告诉你,在Java中,所有方法都用C ++术语隐式定义为virtual
。即:对于给定对象,任何调用的方法始终是属于所构造对象的方法,而不是引用它的变量。
答案 1 :(得分:0)
我没有任何前面的引用,但让我试着解释会发生什么。
我们有
A foo = new B();
这里foo的引用类型是A
。正在实例化的对象是B
类型。
请注意,仅当对象B
满足与A
的is-a关系时,才能编译此语句。由于B extends A
,B
满足与A
的is-a关系。
foo可以调用的方法基于引用类型。因此,foo只能调用A
类型中声明/定义的方法。被调用的实际方法基于对象类型。由于foo
引用的对象的类型为B
,因此将调用对象B
上的方法。因此,正在调用B.compute(int,int)
。
答案 2 :(得分:0)
调用的方法为B.compute(int, int)
,您可以通过尝试轻松找到它。
由于您要求提供描述原因的文档,我将提供。但这并不容易理解。
JLS Sec 15.12.4. Run-Time Evaluation of Method Invocation中描述了这一点。
回想一下,有问题的方法调用是compute(a, 1);
<强> 15.12.4.1。计算目标参考(如有必要)
方法调用的格式为 MethodName
,并且它是非静态的,因此以下情况适用:
否则,让T为该方法所属的封闭类型声明,并且让n为整数,使得T是其声明立即包含方法调用的类的第n个词法封闭类型声明。目标参考是第二个词汇封闭的实例。
所以我们知道我们的目标参考。这是一种冗长的说法,我们将在this
上调用该方法。
<强> 15.12.4.2。评估参数
这是微不足道的,因为参数是int
s。
<强> 15.12.4.3。检查类型和方法的可访问性
是的,可以访问类型和方法。
<强> 15.12.4.4。找到要调用的方法
调用模式为virtual
,如Sec 15.12.3中所述。因此,这适用:
如果调用模式是interface或virtual,则S最初是目标对象的实际运行时类R.
请注意,如果您要在System.out.println(getClass())
行之前立即写compute(a, 1)
,那么当按照问题中显示的方式调用时,它会打印出B
,而不是A
。因此,运行时类R
为B
,因此S
也是B
。
此外:
设X是方法调用的目标引用的编译时类型。
X
是A
。
然后:
类S包含一个名为m的方法的声明,该方法具有相同的描述符
和
调用模式为
virtual
,S
中的声明覆盖X.m
(第8.4.8.1节),然后在S
中声明的方法是调用,程序终止。
因此,调用B
中声明的方法。
这是非常沉重的阅读,老实说,你不需要在这么多细节上知道它。这是我第一次真正费心去挑选那个规范。
要记住的简单规则是:如果在子类中重写该方法,那就是被调用的方法。