虚拟机执行方法,重载与重写

结合前面虚拟机运行时内存分布和Class文件拆解,当一个方法从被调起到执行结束时对应一个栈帧从java方法栈中入栈到出栈。栈帧是用于支持虚拟机进行方法调用和执行的数据结构,其中储存一个方法所拥有的详细信息,包括:局部变量表、操作栈、动态链接和返回地址等信息。如果一个方法具有返回值,在当java方法栈处于栈顶的栈帧出栈后,虚拟机会将返回值压入当前栈顶的栈帧的局部变量内。

局部变量表:用于存放方法参数和方法内部定义的局部变量,单位Slot可以存放boolean, byte, char, short, int, float, reference或returnAddress类型的数据,Slot可重复使用。

操作栈:后入先出栈,数据写入提取,做算术运算时是通过操作栈进行的,在调用方法时也可以通过操作栈进行参数传递。

动态链接:栈帧有一个指向运行时常量池中方法的引用,有的在类加载的解析阶段一部分符号引用会直接转化为直接引用,例外一部分会在每一次运行期间转化为直接引用,这部分称为动态链接。

 

简单方法执行图解

package ysh;

public class TestClass {
	private int m;
	
	public int inc() {
		return m+1;
	}
	
}

使用javap命令查看字节码,Stack=2操作栈深度为2,locals=1局部变量1个,args_size=1方法参数1个(this)

最后ireturn指令将操作栈栈顶int型数据返回,方法执行完成出栈。

 

方法重载与重写

在上节class文件分析中方法表name_inde指向类常量池中方法的符号引用,这些符号引用一部分在类加载解析阶段转化为直接引用。这些方法必须满足“编译器可知,运行期不可变”,如:静态方法, 私有方法, 实例构造器, 父类方法4类。下面方法的重载例子:

package ysh;

public class ReLoad {

	static class Father{
		
	}
	static class Son extends Father{
		
	}
	
	private void say(Son child) {
		System.out.println("Son say");
	}
	private void say(Father father) {
		System.out.println("father say");
	}

	public static void main(String[] args) {
		Father father = new Son();
		ReLoad reLoad = new ReLoad();
		reLoad.say(father);/** father say */
	}
	
	
}

上面的例子运行结果为:

father say

对于Father father = new Son(); Father是father的表面类型,Son是father的真实类型。表面类型在编译器期是可知的;而真实类型在运行期才可以确定。因此,在编译器期,Javac会根据方法传入数的表面类型决定使用哪一个重载方法。

 

下面是一个方法重写的例子:

public class ReWrite {
	
	static class Father{
		public void say() {
			System.out.println("father say");
		}
	}
	
	static class Son extends Father{
		@Override
		public void say() {
			System.out.println("Son say");
		}
	}
	
	static class Son2 extends Father{
		@Override
		public void say() {
			System.out.println("Son2 say");
		}
	}
	public static void main(String[] args) {
			Father father = new Son();
			Father father2 = new Son2();
			father.say();
			father2.say();
	}
}

上面例子运行结果:

Son say

Son2 say

此时father.say()、 father2.say() 并没有根据表面类型来判断调用哪个方法,而是同过真实类型。使用javap查看字节码:

两个方法的调用 17 与 21,从字节码上看完全是一样的,但是两个方法的结果返回不一样。原因在与虚拟机在遇到invokevirtual指令时会有一个多态查找过程:

  • 找到操作栈栈顶元素对象的真实类型;
  • 如果在真实类型中找到与常量中的描述符和简单名称都符合的方法,检验权限,通过返回直接引用,不通过返回权限异常;
  • 没找到配方方法,则按照继承关系从下往上对真实类型的父类就行查找检验;
  • 最终没有找到匹配方法则抛出异常;

方法的重写就是一个在运行阶段确认具体方法的例子,当然为节省性能,大部分虚拟机不会在每次方法运行时却查找目标方法,而是使用了“虚方法表”的优化方法。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值