7

运行时多态性是否总是在方法覆盖时发生,或者仅在方法覆盖期间将子类对象分配给超类变量后调用方法时才会发生?

class A {
    public void myFunc() {
        System.out.println("Something");
    }
}

class B extends A {
    public void myFunc() {
        System.out.println("Something else");
    }

    public static void main (String args[]) {
        A obj = new B();
        obj.myFunc(); //Is only this call resolved at run time?
     
        A obj2 = new A();
        obj2.myFunc(); //Or is this call too resolved at run time?
   
        B obj3 = new B();
        obj3.myFunc(); //Is this call resolved at compile time?
    }
}
4

5 回答 5

6

在方法覆盖中,方法解析总是由 JVM 基于 RUN TIME OBJECT 处理。是的,所有这些调用都将在运行时得到解决。

A obj = new B();
obj.myFunc();
  • 这里引用变量属于 A 类,但对象属于 B 类。因此,将调用 B 类 myFunc 方法

A obj2 = new A();

obj2.myFunc();

  • 这里引用变量属于 A 类,对象也属于 A 类。因此,将调用 A 类 myFunc 方法

B obj3 = new B();

obj3.myFunc();

  • 这里引用变量属于 B 类,对象也属于 B 类,因此,B 类的 myFunc 方法将被调用
于 2020-09-14T04:34:15.523 回答
4

编译器是否真的能够优化三个方法调用中的任何一个或所有并在编译时或将来的某个实现中解析调用是无关紧要的:所有三个方法调用都是运行时多态性的示例,也称为动态调度。具有所谓的静态多态性,也称为方法重载,这是一个具有多个同名但具有不同参数类型的方法的类。

这是一个小型 Java 程序:

public class Test {


    public void foo(String inputString)
    {
        System.out.println(inputString);
    }


    public static void main(String[] args) {
        Test test = new Test();
        test.foo("a"); # very obvious method invocation
    }

}

人类很清楚在运行时会调用什么方法。这是反汇编的字节码:

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void foo(java.lang.String);
    Code:
       0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       3: aload_1
       4: invokevirtual #3                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
       7: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #4                  // class Test
       3: dup
       4: invokespecial #5                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: ldc           #6                  // String a
      11: invokevirtual #7                  // Method foo:(Ljava/lang/String;)V
      14: return
}

指令 11 是虚拟方法的运行时分派。

但正如我所说,即使在将来的某些编译器实现中,这已针对不同的调用进行了优化,那也只是一个实现细节。

于 2020-09-18T15:44:54.830 回答
0
  • 私有、最终和静态成员(方法和变量)使用静态绑定。

  • 而对于虚拟方法(在 Java 中方法默认是虚拟的),绑定是在运行时根据变量所持有的对象完成的。

因此,为了回答您的问题,对于以下所有情况,方法解析始终在运行时完成。(无论是子类的对象分配给超类的变量还是该变量持有的相同类型的对象)

    A obj = new B();
    obj.myFunc();
 
    A obj2 = new A();
    obj2.myFunc();

    B obj3 = new B();
    obj3.myFunc();
于 2020-09-18T08:16:46.077 回答
0

通常,多态性的概念与使用一种类型(类/接口)的不同形式有关,这就是为什么子类需要根据超类型具有类似的契约。

考虑到您的问题,所有三个语句都将在运行时解决,但只有第一个语句属于多态性。

于 2020-09-20T13:09:19.703 回答
0

多态性是面向对象编程中的一个原则:编写软件的逻辑而不关心实现要执行的代码的正确实现是(至少在原则上),总是在运行时确定

你的疑问是关于这个例子:A a = new A(); 但它相当于这个A a = MyFactory.get();

也许 JVM 执行了一些优化final A a = new B();以避免搜索正确的实现?

于 2020-09-18T15:00:08.877 回答