面试题答案
一键面试1. Java多态的动态绑定在运行时确定调用实际方法的过程
在Java中,动态绑定是在运行时基于对象的实际类型来确定调用的实际方法。具体过程如下:
- 编译期检查:编译器首先会检查调用方法的对象引用的声明类型是否具有该方法。如果没有,编译器会报错。例如,假设有如下代码:
class Animal {
public void makeSound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog();
animal.makeSound(); // 编译期检查Animal类是否有makeSound方法
}
}
这里编译器检查Animal
类是否有makeSound
方法,若Animal
类没有此方法,编译器就会报错。
2. 运行期确定实际方法:在运行时,Java虚拟机(JVM)会根据对象的实际类型来确定调用的实际方法。在上述例子中,animal
引用的声明类型是Animal
,但实际创建的对象类型是Dog
。JVM会在Dog
类的方法表(method table)中查找makeSound
方法。方法表是在类加载时创建的,它包含了该类及其所有超类中定义的虚方法(可以被重写的方法)的入口地址。JVM通过对象头中的类型指针找到对象实际类型(这里是Dog
类)的方法表,然后在方法表中查找与所调用方法签名匹配的方法,最终调用Dog
类中的makeSound
方法。
2. 使用final关键字修饰方法对动态绑定性能可能产生的影响
- 禁用动态绑定:当一个方法被
final
关键字修饰时,意味着该方法不能被重写。这就使得编译器在编译时就可以确定调用的方法,而无需在运行时进行动态绑定。例如:
class Bird {
public final void fly() {
System.out.println("Bird is flying");
}
}
class Sparrow extends Bird {
// 这里如果尝试重写fly方法,编译器会报错
}
public class Main {
public static void main(String[] args) {
Bird bird = new Sparrow();
bird.fly(); // 编译时就确定调用Bird类的fly方法
}
}
- 潜在的性能优化:由于无需在运行时进行动态绑定查找实际方法,对于被频繁调用的
final
方法,这可以减少一定的性能开销。特别是在循环或者对性能要求较高的代码块中,这种优化效果可能更明显。同时,final
方法也有助于JVM进行一些优化,比如内联(inlining)。JVM可以将final
方法的代码直接嵌入到调用处,避免了方法调用的栈操作开销,从而进一步提高性能。