###本人新手菜鸡,如有不正确或不完整的地方,还请大家指出来。谢谢!###
例子:
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()中看到了下面的指令:
实例方法 f1() 在调用实例方法 f2() 时用到了虚拟机指令 invokevirtual,这个指令有什么作用呢?然后我查了查周志明的《深入理解Java虚拟机》这本书,里面是这么说的(以下全部来自此书):
invokevirtual 指令运行时解析过程大致分为以下几个步骤:
- 找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C;
- 如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;若不通过,则返回java.lang.IllegalAccessError异常;
- 否则,按照继承关系从下往上一次对C的各个父类进行第2步中的搜索和验证过程;
- 如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。
这也即是在运行时根据实际类型确定方法执行版本的动态分配过程,也是Java中方法重写的本质。