MST

星途 面试题库

面试题:Java多态下重载方法的字节码层面分析

已知有如下Java类结构: ```java class Base { public void performAction() { System.out.println("Base action"); } public void performAction(int num) { System.out.println("Base action with int: " + num); } } class Derived extends Base { @Override public void performAction() { System.out.println("Derived action"); } public void performAction(double num) { System.out.println("Derived action with double: " + num); } } ``` 请从字节码层面分析,当执行`Base obj = new Derived(); obj.performAction();` 和 `Derived obj2 = new Derived(); obj2.performAction(5);` 这两个语句时,Java虚拟机是如何确定具体调用哪个方法的。包括但不限于方法查找的过程、涉及到的字节码指令以及多态和重载在字节码层面的体现。
22.1万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试

1. Base obj = new Derived(); obj.performAction();

  • 方法查找过程
    • 首先,new Derived() 创建了一个 Derived 类的实例对象。
    • 然后,Base obj 将这个 Derived 实例赋值给 Base 类型的引用。这里发生了向上转型,虽然引用类型是 Base,但实际对象类型是 Derived
    • 当调用 obj.performAction() 时,由于Java的动态绑定机制(多态),JVM会在运行时根据对象的实际类型(Derived)来查找方法。具体来说,从 Derived 类开始查找是否有匹配的 performAction() 方法,如果没有则沿着继承链向上查找。这里 Derived 类重写了 performAction() 方法,所以会调用 Derived 类中的该方法。
  • 字节码指令
    • new 指令用于创建 Derived 类的新实例,并将其引用压入操作数栈。
    • dup 指令复制操作数栈顶元素,为后续调用构造函数做准备。
    • invokespecial 指令用于调用 Derived 类的构造函数,初始化新创建的对象。
    • astore_1 将对象引用存储到局部变量表的索引1位置(假设 obj 是第一个局部变量)。
    • 当调用 obj.performAction() 时,会使用 invokevirtual 指令。invokevirtual 指令会根据对象的实际类型在运行时动态查找并调用方法。它首先会在对象的实际类型(Derived)的虚方法表(vtable)中查找 performAction() 方法的入口地址,然后调用该方法。
  • 多态在字节码层面的体现
    • 多态通过 invokevirtual 指令实现。该指令在运行时根据对象的实际类型来动态绑定方法,而不是根据引用的静态类型。在这个例子中,尽管 obj 的静态类型是 Base,但由于实际对象类型是 Derivedinvokevirtual 指令会调用 Derived 类中的 performAction() 方法,体现了多态特性。

2. Derived obj2 = new Derived(); obj2.performAction(5);

  • 方法查找过程
    • 同样,new Derived() 创建 Derived 类的实例对象,并赋值给 Derived 类型的引用 obj2
    • 当调用 obj2.performAction(5) 时,因为参数类型是 int,JVM会在 Derived 类中查找与参数列表匹配的方法。这里 Derived 类没有直接定义 performAction(int) 方法,所以会沿着继承链向上查找,在 Base 类中找到了 performAction(int) 方法,因此调用 Base 类中的该方法。
  • 字节码指令
    • 前面创建对象部分(newdupinvokespecialastore_2,假设 obj2 是第二个局部变量)与第一个例子类似。
    • 当调用 obj2.performAction(5) 时,同样使用 invokevirtual 指令。由于方法参数为 int,JVM会根据方法名和参数列表在 Derived 类及其父类的虚方法表中查找匹配的方法。找到 Base 类中的 performAction(int) 方法后,调用该方法。
  • 重载在字节码层面的体现
    • 重载通过方法名相同但参数列表不同来实现。在字节码层面,invokevirtual 指令在查找方法时不仅考虑方法名,还会严格匹配参数列表。当调用 obj2.performAction(5) 时,JVM根据参数类型 int 来确定具体调用哪个 performAction 方法,这体现了重载的特性。如果在 Derived 类中有一个 performAction(int) 方法,就会调用 Derived 类中的该方法,而不是 Base 类中的。