编译器如何决定重载和重写?

Kar*_*ria 3 java overriding overloading javac

class A {
    public void doSomething(float f) {
        //print "in A"
    }
}

class B extends A {
    public void doSomething(int i) {
        // print "in B"
    }

    public static void main(String[] args) {
        A a = new B();
        a.doSomething(10);
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器首先检查doSomething()A 类中是否存在。一旦确认,在运行时,将检查对象类型并执行相应的方法,该方法doSomething()应该执行 B 类的方法,但会执行doSomething()类的方法。A

如果两个方法相同,则只会B doSomething()执行类。编译器如何决定某个方法被重写,从而由 JVM 处理?

ern*_*t_k 6

简而言之:

  1. 编译器选择将被调用的方法的签名
  2. 运行时选择将运行编译器选择的签名的哪个实现

在步骤 1 中,编译器查看调用该方法的对象的声明类型以及参数类型,这意味着:

  • 编译器只知道是aA“声明类型”,又名静态类型);因此它只能搜索 中的方法A或由 继承的方法A。这意味着编译器甚至不会在您的示例中搜索doSomethingin class B
  • 它查看参数的数据类型来解析重载中的一种方法。在您的示例中,这不是必需的,因为只有一种doSomething方法(带有float参数的方法)

根据以上内容,编译器只能得出结论:它将doSomething(float)运行。您可以将其视为编译器选择的方法签名是一成不变的,运行时无法更改它。

在步骤 2 中,运行时知道将运行哪个签名 ( doSomething(float)),它唯一需要做的就是选择将调用该签名的哪个实现。new B()为此,它会查看示例中的实际对象类型(创建的对象) 。然后,如果被重写B,它将运行 中的实现,或者在树中搜索该确切签名的任何被重写的实现,直到到达 中继承的实现。因为不覆盖,所以继承的实现运行。ABdoSomething(float)A.doSomething(float)A