java有继承关系的方法调用

###本人新手菜鸡,如有不正确或不完整的地方,还请大家指出来。谢谢!###

例子:

class A{
    public static void s1(){
        System.out.println("This is A.static.s1");
    }

    public void f1(){
        System.out.println("This is A.f1");
        f2();
        s1();
    }
    public void f2(){
        System.out.println("This is A.f2");
    }
}

class B extends A{
    public static void s1(){
        System.out.println("This is B.static.s1");
    }

    @Override
    public void f2(){
        System.out.println("This is B.f2");
    }
}

public class Test {
    public static void main(String[] args) {
        B b=new B();
        b.f1();
    }
}

输出结果为:

This is A.f1
This is B.f2
This is A.static.s1

输出 This is A.static.s1 很好理解,因为 static 方法无法被子类覆盖,所以A中的 f1() 方法调用静态方法 s1() 只能是A中的实现。但是调用的实例方法 f1() 却不是A中的实现,而是子类B中的实现,这是为什么呢?

最近刚好看了一点点Java字节码相关知识,那就学以致用。

在CMD中敲入 javap -verbose path 命令查看类A编译好的 .class 文件,然后在A的方法f1()中看到了下面的指令:

java有继承关系的方法调用

 

实例方法 f1() 在调用实例方法 f2() 时用到了虚拟机指令 invokevirtual,这个指令有什么作用呢?然后我查了查周志明的《深入理解Java虚拟机》这本书,里面是这么说的(以下全部来自此书):

invokevirtual 指令运行时解析过程大致分为以下几个步骤:

  • 找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C;
  • 如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;若不通过,则返回java.lang.IllegalAccessError异常;
  • 否则,按照继承关系从下往上一次对C的各个父类进行第2步中的搜索和验证过程;
  • 如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。

这也即是在运行时根据实际类型确定方法执行版本的动态分配过程,也是Java中方法重写的本质。