JVM:方法的“重写”和重载在虚拟机中是如何实现的?虚拟机的分派问题。

简述方法的“重载”和“重写”

  • 重载(Overload):在一个类中,多个方法的方法名相同,而参数个数、参数类型和参数顺序不同。在调用方法时,通过不同的参数列表,来确定调用的方法。这体现了多态。
  • 重写(Override):在子类继承父类时,通过方法名、返回值类型和参数列表相同,来覆盖父类的方法,从而改变方法的行为。

“重载”和“重写”的原理是什么?“重载”时如何确定方法版本?“重写”时父类如何确认知道要调用子类的方法?

首先明确一个概念,对于下面的代码:

Human man = new Man();
Human woman = new Woman();
sr.sayHello(man);
sr.sayHello(woman);

其中,Human称为变量的“静态类型”,Man称为变量的“实际类型”
上面两个sayHello,最终调用的其实都是sayHello(Human)方法。
因为,虚拟机(准确说时编译器)在重载时,是根据参数的静态类型而不是实际类型作为判断依据的。也就是说,由于静态类型在编译期可知,在编译阶段,编译器就会根据方法参数的静态类型决定使用哪个重载方法了。
依靠静态类型决定方法版本的分派,叫做静态分派

再看“重写”,先看下面代码:

class DynamicDisoatch{
	static abstract class Human{
		protected abstract void sayHello();
	}
	
	static class Man extends Human{
		@Override
		protected abstract void sayHello(){
			System.out.println("man say hello")
		}
	}

	static class Woman extends Human{
		@Override
		protected abstract void sayHello(){
			System.out.println("woman say hello")
		}
	}

	public static void main(String[] args){
		Human man = new Man();
		Human woman = new Woman();
		man.sayHello():
		women.sayHello();
		man = new Woman();
		man.sayHello();
	}

}

最后输出:

man say hello
woman say hello
woman say hello

毫无疑问,都是按“实际类型”进行方法的调用。
因为方法调用的invokevirtual指令执行的第一步是确定方法接收者(这里就是man.sayHello中的man)的实际类型,然后根据方法接收者的实际类型来选择方法的版本。这里的实际类型分别是Man和Woman,所以最后执行Man和Woman中的方法。
这种在运行期根据实际类型确定方法执行版本的分派,称为动态分派

总而言之:

  • 重载是静态分派,根据静态类型选择方法版本
  • 重写是动态分派,根据实际类型选择方法版本

你可能感兴趣的:(JVM)