[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!
时间: 2024-10-11 07:42:46

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

[jvm解析系列][十三]字节码指令小节,从字节码看JVM的栈解释器执行过程。

众所周知,JVM以前一直采用的是解释执行,但是后来在历代的版本更迭中也加入了编译执行.所以总的来说JVM是包含了解释执行和编译执行.这一部分不属于JVM的范畴了,已经属于编译了,大多数都是进行词法分析之类的,以后有时间会补充. 同时大家都知道现在大体上分为两种指令集架构,第一种就是基于栈的第二种是基于寄存器的,简单点说,基于寄存器的架构速度更快,但是可移植性不强,但是基于栈的指令集架构虽然慢,但是可移植性很强,大家都知道java本身就是依靠可移植性出名的,所以无可争议的使用了栈的指令集架构.(也

[jvm解析系列][十]类加载器和双亲委派模型,你真的了解ClassLoader吗?

上一章我们讲到,一个类加载到内存里我们可以操作的部分只有两个,一个是加载部分一个是static{},我相信static{}不用多讲了. 接下来我们就来解析一下ClassLoader即类加载器,他就是用来加载字节码到方法区的类. 当年出现ClassLoader这个东西动态加载类的字节码主要还是为了满足JavaApplet的需求.虽然后来JavaApplet挂掉了,但是ClassLoader这个形式还是保留了下来,而且活的很好. 类的相等和instanceOf: 来我们来写一个例子 public c

[jvm解析系列][九]类的加载过程和类的初始化。你的类该怎么执行?为什么需要ClassLoader?

通过前面好几章的或详细或不详细的介绍,我们终于把字节码的结构分析的差不多了.现在我们面临这样一个问题,如何运行一个字节码文件呢? 首先,java语言不同于其他的编译时需要进行链接工作的语言不通,java语言有一个很明显的特性,那就是动态加载,一个字节码的加载往往都是在程序运行的时候加载进来的,很多时候这种方式给我们带来了便利.虽然从某种意义上来说他可能消耗了一定的资源降低了性能. 类的生命周期? 没错,一个类的生命周期,在很多人眼里可能类天生都摆在那里了,随着程序生,随着程序死.但是事实情况并不

[jvm解析系列][十一]字节码执行之栈帧,你的字节码是如何运行的?

在之前的章节中我们讲解了jvm的内存分配和管理,class的文件结构,就差之行了.那么从第十一章开始我们就开始讲java虚拟机是如何执行一个class文件的. 首先我们应该明确虚拟机是区别于物理机的一种说法,物理机的执行引擎是建立在处理器,硬件 ,指令集之上的.而我们的虚拟机则由自己实现.在虚拟机中大致分为两种执行方式:解释执行和编译执行. 我们之前讲过,虚拟机运行方法的时候运行在java虚拟机栈里面,里面的存储结构是栈帧,需要了解一个虚拟机如何运行字节码文件的,首先我们需要了解一个栈帧的结构.

[jvm解析系列][十四]动态代理和装饰模式,带你看源码深入理解装饰模式和动态代理的区别。

不知道大家知不知道设计模式中有一种叫做装饰,举一个简单的例子. 一天一个年轻领导小王讲话:咳咳,我们一定要xxx抓紧xxxx学习xxx的精神!好,今天的会议结束! 然后有一个老领导李同志接过来说:那个我在补充两点,个别同志xxx,一定要注意xxx.好散会. 然后另一天小王同志又在讲话:xxx两手都要抓,xxxx一定要注意. 这个时候老周同志出来了:嗯,小王讲的很好,我还有几点要补充xxxx. 那么很明显,小王同志的讲话方法不是很让人满意,那么老李领导或者老周领导可以接过来继续装修一下.其实这就是

[jvm解析系列][八]方法表集合,Code属性和Exceptions属性,你的字节码存在哪里了?

根据我们第五章的总构图来看,这一章我们正该讲到方法表集合: 大家可能注意到在java中声明一个方法和声明一个变量很相似,public int a = 0;和public int a(){};于是在方法表集合中和字段表集合也很相似. 一个方法表的结构应当和下图一样: 对比字段表应该发现几乎是一样的.access_flags里的可选项略有不同而已. access_flags: 这样以来我们把方法表和字段表对比来看应该很好理解了.对于属性表又是一大块内容.上次我们说到了属性表的结构 并且说了在字段表中

sdut 面向对象程序设计上机练习十二(运算符重载)

面向对象程序设计上机练习十二(运算符重载) Time Limit: 1000MS Memory limit: 65536K 题目描述 处理一个复数与一个double数相加的运算,结果存放在一个double型变量d1中,输出d1的值.定义Complex(复数)类,在成员函数中包含重载类型转换运算符:operator double(){return real;} 输入 输入占两行: 第1行是一个复数的实部和虚部,数据以空格分开. 第2行是一个实数. 输出 输出占一行,复数的实部和实数之和,小数点后保

分析Java的类加载器与ClassLoader(二):classpath与查找类字节码的顺序,分析ExtClassLoader与AppClassLoader的源码

先回顾一下classpath classpath的作用: classpath的作用是指定查找类的路径:当使用java命令执行一个类(类中的main方法)时,会从classpath中进行查找这个类. 指定classpath的方式一:         设置环境变量CLASSPATH,多个路径之间使用英文的分号隔开,也可以指定为jar包路径.          示例:CLASSPATH=c:/myclasses/;c/mylib/aa.jar;c:/mylib/bb.jar;.          注意

一张图看懂阿里云网络产品[十二]云企业网

摘要: 阿里云致力于为用户提供优质.高效.稳定的网络传输环境,云企业网(Cloud Enterprise Network)将提供一种能够快速构建混合云和分布式业务系统的全球网络,帮助用户打造一张具有企业级规模和通信能力的云上网络. 原文链接 阅读更多干货好文,请关注扫描以下二维码: 原文地址:http://blog.51cto.com/13641484/2090361