java在运行时由对象重载方法调用顺序

时间:2016-11-25 00:44:46

标签: java oop

考虑以下类层次结构:

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文档的任何引用?

3 个答案:

答案 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 AB满足与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 。因此,运行时类RB,因此S也是B

此外:

  

设X是方法调用的目标引用的编译时类型。

XA

然后:

  

类S包含一个名为m的方法的声明,该方法具有相同的描述符

  

调用模式为virtualS中的声明覆盖X.m(第8.4.8.1节),然后在S中声明的方法是调用,程序终止。

因此,调用B中声明的方法。

这是非常沉重的阅读,老实说,你不需要在这么多细节上知道它。这是我第一次真正费心去挑选那个规范。

要记住的简单规则是:如果在子类中重写该方法,那就是被调用的方法。