面试题答案
一键面试1. invokevirtual、invokeinterface指令利用方法表实现多态调用的原理
- invokevirtual指令
- 原理:该指令用于调用对象的实例方法。在执行
invokevirtual
指令时,首先会根据对象的实际类型找到对应的类。每个类在加载到JVM时,都会创建一个方法表(method table
),方法表中存放着该类及其父类中所有可被子类重写的实例方法的直接引用。 - 流程:
- 操作数栈顶弹出对象引用。
- 根据对象实际类型确定要调用方法所在的类。
- 在该类的方法表中查找与指令参数中指定的方法签名(方法名和参数列表)匹配的方法指针。
- 调用该指针指向的方法。这样就实现了根据对象实际类型动态调用合适的重写方法,从而支持多态。
- 原理:该指令用于调用对象的实例方法。在执行
- invokeinterface指令
- 原理:用于调用接口方法。与
invokevirtual
类似,但接口方法的查找更为复杂。每个实现了接口的类同样有一个方法表,接口方法在方法表中的位置与接口定义中方法的顺序有关。 - 流程:
- 从操作数栈顶弹出对象引用。
- 检查对象实际类型是否实现了指定接口。
- 在对象实际类型对应的方法表中查找接口方法的入口。由于一个类可能实现多个接口,所以查找过程需要遍历接口方法在方法表中的特定位置,以确定要调用的具体方法。这同样实现了基于对象实际类型的多态调用,因为不同实现类对接口方法的实现不同。
- 原理:用于调用接口方法。与
2. Java不同版本中方法表结构及字节码指令操作方式的变化
- 早期版本(如Java 1.0 - 1.2)
- 方法表结构:方法表相对简单,直接包含类及其父类中可重写实例方法的指针。每个方法在方法表中的位置相对固定,基于方法声明的顺序。
- 字节码指令操作:
invokevirtual
和invokeinterface
指令按照上述基本原理操作方法表,在类加载时构建方法表,运行时根据对象类型在方法表中查找方法。
- Java 5.0 引入泛型后
- 方法表结构:方法表的结构基本保持不变,但由于泛型的引入,字节码中增加了一些额外的类型信息。在编译时,泛型类型会被擦除,但是方法表中的方法签名可能需要进行一些调整以处理泛型相关的类型转换等操作。
- 字节码指令操作:
invokevirtual
和invokeinterface
指令在处理泛型相关方法调用时,需要结合新的类型信息进行方法查找和调用。例如,在调用泛型方法时,可能需要额外的字节码指令来确保类型安全,不过方法表的基本查找逻辑未变。
- Java 7 动态语言支持(invokedynamic指令引入)
- 方法表结构:方法表结构本身未因
invokedynamic
指令的引入而直接改变,但invokedynamic
指令开启了一种新的动态方法调用机制。 - 字节码指令操作:
invokedynamic
指令与invokevirtual
、invokeinterface
不同,它基于运行时动态解析方法调用,允许在运行时动态生成调用点和绑定方法。而invokevirtual
和invokeinterface
仍然基于类的静态结构(方法表)进行方法调用,不过invokedynamic
的出现丰富了Java的动态调用能力,与传统基于方法表的静态多态调用形成互补。
- 方法表结构:方法表结构本身未因
- Java 8 方法句柄(Method Handles)
- 方法表结构:方法表结构依旧保持稳定。然而,方法句柄为方法调用提供了一种更灵活的机制。方法句柄可以直接引用方法表中的方法,但也可以基于方法句柄创建新的调用形式,这在一定程度上扩展了方法调用的灵活性。
- 字节码指令操作:虽然
invokevirtual
和invokeinterface
指令基本操作方式未变,但方法句柄的相关操作(如invokevirtual
、invokeinterface
对应的方法句柄操作)为方法调用提供了更多动态性,例如可以在运行时根据不同条件选择不同的方法句柄来调用方法表中的方法,进一步增强了多态调用的灵活性和动态性。