zoukankan      html  css  js  c++  java
  • 方法调用解析

    方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程.由于Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里存储的只是符号引用,而非方法在实际运行时内存布局中的入口地址.该特性为Java带来了更强大的动态扩展能力.

    解析

    所有方法调用中,目标方法在Class文件里都是一个常量池中的符号引用,在类加载的解析阶段,会将其中一部分符号引用转化为直接引用--此情形的前提是:方法在程序实际运行前就有一个可确定的调用版本,且这个调用版本在运行期间是不可改变的.这类方法的调用成为解析(Resolution).

    在Java语言中,符合"编译期可知,运行期不可变"要求的方法主要有静态方法和私有方法两大类,前者与类型相关联,后者在外部不可访问,这两种方法都不可能通过继承或别的方式重写出其他版本,因此它们都适合在类加载阶段进行解析.

    与之相对应,在Java虚拟机中提供了四条方法调用字节码指令,分别是:

    invokestatic 调用静态方法

    invokespecial 调用实例构造器<init>方法,私有方法和父类方法

    invokevirtual 调用所有虚方法

    invokeinterface 调用接口方法,会在运行时再确定一个实现此接口的对象

    只要能被invokestatic和invokespecial指令调用的方法,都可以在解析阶段确定唯一的调用版本,符合此条件的有静态方法,私有方法,实例构造器和父类方法四类.它们在类加载时即把符号引用解析为该方法的直接引用.这些方法可以称为非虚方法,与之相反,其他方法称为虚方法(除final方法外).

    以下代码中,静态方法sayHello()只可能属于类型StaticResolution,没有任何手段可以覆盖或隐藏该方法:

    1 public class StaticResolution {
    2     public static void sayHello() {
    3         System.out.println("Hello world");
    4     }        
    5     
    6     public static void main(String[] args) {
    7         StaticResolution.sayHello();
    8     }
    9 }

    使用javap命令查看这段程序的字节码,发现的确是通过invokestatic命令调用sayHello()方法的:

    public static void main(java.lang.String[]);
        Code:
         Stack=0, Locals=1, Args_size=1
         0:    invokestatic    #31; //Method sayHello:()V
         3:    return
        LineNumberTable:
         line 15: 0
         line 16: 3

    虽然final方法是使用invokevirtual指令来调用的,但由于它无法被覆盖,没有其他版本,所以也无须对方法接收者进行多态选择,或者说多态选择的结果肯定是唯一的.在Java语言规范中明确说明了final方法是一种非虚方法.

    读周志明的<深入理解Java虚拟机>一书第8.3章节, 备忘

  • 相关阅读:
    μC/OS-III---I笔记10---内存管理
    μC/OS-III---I笔记9---任务等待多个内核对象和任务内建信号量与消息队列
    μC/OS-III---I笔记8---事件标志
    二.java下使用RabbitMQ实现hello world
    (转)rabbitMQ基础知识及命令语句
    一.windows环境下rabbitMQ的的安装和配置
    spring mvc对静态资源的访问
    2017年12月计划
    java两种动态代理方式的理解
    log4j配置文件详解(转)
  • 原文地址:https://www.cnblogs.com/fangzhaolee/p/2839418.html
Copyright © 2011-2022 走看看