java - Java 中的方法绑定(bind)

标签 java jvm overloading overriding

最近在阅读Thinking in Java(第 4 版)时,我遇到了一个关于 Java 中方法绑定(bind)的问题。首先让我们看一下书中的两个定义:

  1. 将方法调用连接到方法体称为绑定(bind)。
  2. Java 中的所有方法绑定(bind)都使用后期绑定(bind),除非方法是静态的或最终的。

您可以在多态性一章的方法调用绑定(bind)部分找到这些定义。 (第 281-282 页)

为了证明这一点,我写了下面的代码:

public class Test3{
    public static void main(String[] args) {
        BindingTest_Sub sub1 = new BindingTest_Sub();
        BindingTest_Base sub2 = new BindingTest_Sub();

        sub1.ovrLd(new Integer(1));       //  statement 1
        sub2.ovrLd(new Integer(2));       //  statement 2
        sub2.ovrRd();                     //  statement 3
    }
}

class BindingTest_Base {
    void ovrLd(Object obj){
        System.out.println("BindingTest_Base ovrLd()");
    }
    void ovrRd(){
        System.out.println("BindingTest_Base ovrRd()");
    }
}

class BindingTest_Sub extends BindingTest_Base{
    void ovrLd(Integer i){
        System.out.println("BindingTest_Sub ovrLd()");
    }
    void ovrRd(){
        System.out.println("BindingTest_Sub ovrRd()");
    }
}

执行结果为:

BindingTest_Sub ovrLd()
BindingTest_Base ovrLd()
BindingTest_Sub ovrRd()

根据这个结果,我有以下问题:

  1. 根据书上的定义,由于我的所有方法都是可继承的,Java 将对所有三个语句使用后期绑定(bind)(动态绑定(bind))。但是,我看了一些其他文章,其中说Java在处理重载时使用静态绑定(bind)。这似乎是矛盾的,因为显然陈述 1 是重载的。
  2. 我不完全理解为什么Java在语句2中调用基类的ovrLd()。如果它使用动态绑定(bind),它应该调用子类的overLd(),因为运行时JVM应该清楚sub2是一个实例BindingTest_Sub 类。另一方面,如果它使用静态绑定(bind),它还应该调用子类的 overLd(),因为编译器能够观察到给定参数的类型是 Integer。你能告诉我编译器或 JVM 在处理语句 2 时做了什么工作吗?
  3. 陈述 3 的结果对我来说很有意义。但是,我仍然很好奇编译器如何将它 (ovrRd()) 识别为覆盖方法。换句话说,编译器如何知道有另一个类有一个覆盖这个 ovrRd() 的方法。

如果您对上述问题或 Java 方法绑定(bind)机制有任何想法,我们将不胜感激。也请随时指出我的错误。

最佳答案

TL;DR; 您并没有真正重载 ovrLd(Object),不是在运行时。编译器使用编译时类型信息来决定调用哪个虚方法是最好的。 sub1sub2 有不同的编译时类型。 sub1 的类型与 ovrLb(Integer) 有不同的最佳匹配。

解释。你想知道:

  • 如果 sub1.ovrLd(new Integer(1)) 调用 BindingTest_Sub.ovrLd(Integer)
  • 那为什么 sub2.ovrLd(new Integer(2)) 调用 BindingTest_Base.ovrLd(Object)

在这种情况下它是这样工作的:

编译器使用变量的编译时 类型信息来决定调用哪个方法。 sub2 的编译时类型是 BindingTest_Base,在运行时你正在为其分配一个 BindingTest_Sub,但这与编译器无关。

BindingTest_Base 中与该调用的参数匹配的唯一方法是:BindingTest_Base.ovrLd(Object)

因此编译器发出对 orvLd(Object) 的虚拟调用

现在,虚拟调用的运行时方法是根据被调用方法的完整签名(名称+参数)决定的。 BindingTest_Sub 中的 ovrLd(Object) 没有重载 所以基类方法被调用。

sub1 编译器有更多信息。 sub1 的编译时类型是BintdingTest_Sub 并且在该类中有一个匹配ovrLd(Integer) 的方法。

查看字节码你可以清楚地看到:

aload 1   // sub1
// ... blah blah blah creating the integer
// the last opcode issued by the compiler for "statement 1"
INVOKEVIRTUAL com/ea/orbit/actors/samples/helloworld/BindingTest_Sub.ovrLd (Ljava/lang/Integer;)V

aload 2  // sub2
// ... blah blah blah creating the integer
// the last opcode issued by the compiler for "statement 2"
INVOKEVIRTUAL com/ea/orbit/actors/samples/helloworld/BindingTest_Base.ovrLd (Ljava/lang/Object;)V

关于java - Java 中的方法绑定(bind),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29381292/

相关文章:

java - 如果 Xms5120m,jvm 会分配 5G RAM

java - 缩小幸存者空间导致持续的完整 GC

c++ - 运算符重载 "+"和 "="问题 -- school asst

java - 查找具有最新信息的文件

java - JSTL 标签从数据类打印在 JSP 中

java - 如何使用具有简单日志语句的 Spock 测试 Java 中的 catch block

java - Java5/Java6 应用程序到 Java 7 的周到迁移计划?

java - Spring继承不起作用

c++ - 为什么 NULL 被转换成 string*?

c# - 当接口(interface)中有重载方法时,为什么我的代码会调用错误的方法