[jvm解析系列][十二]分派,重载和重写,查看字节码带你深入了解分派的过程。

Posted 胖子程序员

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[jvm解析系列][十二]分派,重载和重写,查看字节码带你深入了解分派的过程。相关的知识,希望对你有一定的参考价值。

重载和重写是分派中的两个重要体现,也是因为这个原因我们才把重载和重写写在了标题上。这一章我们的很多部分都在代码试验上。

总的来说分派分为静态分派动态分派两种。



静态分派

首先我们来看一段源码:

public class Dispatch 
	public static void main(String[] args)
		Animal a = new Dog();
		Animal b = new Cat();
		sound(a);
		sound(b);
	
	public static void sound(Animal a)
		System.out.println("....");
	
	public static void sound(Dog a)
		System.out.println("wang..");
	
	public static void sound(Cat a)
		System.out.println("miao..");
	
	static class Animal
		
	
	static class Dog extends Animal
		
	
	static class Cat extends Animal
		
	
这段源码就是写了三个重载的方法,根据参数的不同输出不同的信息。应该大家都能知道正确输出,输出如下:

....
....
Animal  a = new Dog();在这个里面我们把Animal叫做外观类型(静态类型)把Dog叫做实际类型。静态类型是在编译期可知的,但是一个对象的实际类型要在运行期才可知。而jvm在重载的时候通过参数的静态类型作为判定依据。我们来看一下字节码是不是和我们想的一样


看到图中invokestatic的字符引用,我们应该就知道了编译时期确实完成了方法的定位。

而这些需要靠静态类型定位方法的情形称为静态分派,静态分派发生在编译阶段。也就是说在这个方法执行之前仅仅在编译阶段,sound方法就认定了最后要输出一个"...."而不是wang和miao。





动态分派

我们依然先给一段代码:

public class Dispatch 
	public static void main(String[] args)
		Animal a = new Dog();
		Animal b = new Cat();
		a.sound();
		b.sound();
	
	static class Animal
		public void sound()
			System.out.println("......");
		
	
	static class Dog extends Animal
		public void sound()
			System.out.println("wang!");
		
	
	static class Cat extends Animal
		public void sound()
			System.out.println("miao!");
		
	
这一段代码的输出结果相信我不用贴大家都明白。
wang!
miao!
但是,为什么jvm能够找到这一段方法并且执行的呢?

我们来看一下javap的输出信息


图中画红线的两个部分就是编译后的字节码文件了,他们调用的依然是animal的sound方法,看来动态分派不是在编译期完成的。问题就出在了invokevirtual上面了

jvm运行invokevirtual方法有一个过程,大致如下。

1、找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C。

2、如果C与常量中的描述符和简单名称都符合的方法,说明这个方法就是我们找的,只需要进行访问权限校验(看看是不是private之类)。

3、如果不符合描述符或简单名称。则对C的父类进行第二步中的搜索。

4、最终都没有找到抛出AbstractMethodError。

可能第3步比较难懂,我们再来仔细说明一下

如果我们把代码修改如下:

public class Dispatch 
	public static void main(String[] args)
		Animal a = new Dog();
		Animal b = new Cat();
		a.sound();
		b.sound();
	
	static class Animal
		public void sound()
			System.out.println("......");
		
	
	static class Dog extends Animal
		public void sound(String a)//修改后的方法
			System.out.println("wang!");
		
	
	static class Cat extends Animal
		public void sound()
			System.out.println("miao!");
		
	
那么很明显上述中的C在Animal a = new Dog();中就是Dog类,Dog类中只有一个sound(string)的方法并不存在一个sound()方法,所以去C的父类中即Animal类中找到sound方法,最终输出如下:

......
miao!


以上是关于[jvm解析系列][十二]分派,重载和重写,查看字节码带你深入了解分派的过程。的主要内容,如果未能解决你的问题,请参考以下文章

JVM学习笔记--方法调用之静态分配和动态分配

JAVA基础

访问者模式

(三十)分派调用:静态分派和动态分派

Java重载和重写

JVM 方法调用之静态分派