赞
踩
我们写的代码,经过编译、经过类加载的各种阶段,进入了 JVM 的运行时数据区。
但作为程序员真正关心是代码的执行,代码的执行其实本质上是方法的执行,站在 JVM 的角度归根到底还是 字节码的执行 。
main 函数是 JVM 指令执行的起点,JVM 会创建 main 线程来执行 main 函数,以触发 JVM 一系列指令的执行,真正地把 JVM 跑起来。
接着,在我们的代码中,就是方法调用方法的过程,所以了解方法在 JVM 中的调用是非常必要的。
一个方法的执行是通过调用字节码指令实现的,并且在Class常量池中有类的版本、字段、 方法 和接口等描述信息。即在Java类尚未加载的时候,方法以字节码的形式存在于Class常量池中。
这样说好像不能信服,我们随便写一个方法,通过 jclasslib
(一个查看字节码的工具)来查看,如下:
我们知道了方法在哪里,但是怎么调用呢?关于方法调用,Java共提供了5个指令,来调用不同类型的方法:
invokestatic
, 用来调用静态方法。invokespecial
,用来调用私有实例方法、构造器、super关键字等。invokevirtual
, 用于调用非私有实例方法,比如 public 和 protected,大多数方法调用属于这一种。invokeinterface
,和 invokevirtual
类似,但作用于接口类。invokedynamic
, 用于调用动态方法。我们经常说的静态方法,实例方法等,实际上它们有一个比较官方的说法: 虚方法以及非虚方法。
什么叫非虚方法?
如果方法在编译期就确定了具体的调用版本,这个版本在 运行时是不可变的 ,这样的方法称为非虚方法。 一般来说包含以下五种:
静态方法(static修饰)
私有方法(private修饰)
父类方法
构造方法
final
修饰的方法(特例,因为被final修饰的方法就是不可变的方法,但实际还是使用 invokevirtual
指令),如下
public final void invokeStatic() { System.out.println("invokestatic 调用静态方法"); } 复制代码
简单来说就是被 invokestatic
和 invokespecial
指令调用的方法,我们来验证一下。
调用静态方法:
public class InvokeStatic { public static void invokeStatic() { System.out.println("invokestatic 调用静态方法"); } public static void main(String[] args) { //调用静态方法 InvokeStatic.invokeStatic(); } } 复制代码
查看字节码:
我们可以看到被在main方法的字节码中有: invokestatic #5
invokestatic
我们知道,调用的是静态方法, #5
代表什么呢?我们通过 javap -v InvokeStatic.class
查看,发现 #5
后面有个注释,即 invokeStatic()
方法。
这个方法调用在编译期间就明确以常量池项的形式固化在字节码指令的参数之中了。
调用私有实例方法、构造器、super关键字等。
还是上面的代码,通过实例化:
public class InvokeStatic { public static void invokeStatic() { System.out.println("invokestatic 调用静态方法"); } public static void main(String[] args) { //实例化 Inv
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。