class文件中的access flag记录了一个类的相关信息,比如public abstract final等等。其中有一个值ACC_SUPER。现理解如下:
ACC_SUPER是用来表示如何调用父类的方法。在jdk1.1之前,jvm使用一种invokenonvirtual的指令,调用父类方法。这个方法就是现在的invokespecial 前身。既然连名字都改过了,他们的实现差距可以认为已经比较大了。invokenonvirtual是不会进行虚函数查找的,也就是总是静态绑定。
我们知道,在class文件中使用CONSTANT_Methodref_info来表示一个方法。CONSTANT_Methodref_info中有一个指向类的成员,invokenonvirtual会直接使用CONSTANT_Methodref_info中的类进行方法调用,而不去进行虚函数查找。因此,需要由编译器在编译时就绑定到最近的父类。jdk1.1以后,jvm会忽略CONSTANT_Methodref_info 中的class,转而去查找最近的超类方法。
这个改动是非常有意义的,我们来一下下面的代码:
有一个组件A
1 2 3 4 5 6 7 |
public class GrandParent { protected void myMethod() { // ... } } public class Parent extends GrandParent { } |
组件B使用了组件A
1 2 3 4 5 6 |
public class Child extends Parent { protected void myMethod() { // ... super.myMethod(); } } |
很明显,组件B肯定是用了GrandParent.myMethod()。因为Parent里没有myMethod方法。但是,如果你更新了组件A
1 2 3 4 5 6 7 8 9 10 11 |
public class GrandParent { protected void myMethod() { // ... } } public class Parent extends GrandParent { protected void myMethod() { // ... super.myMethod(); } } |
这个时候,如果使用invokenonvirtual调用方式,你依然会运行GrandParent.myMethod()(在不重新编译Child的情况下)。因为这是在编译时绑定的。使用了CONSTANT_Methodref_info里的class作为调用基准。这个结果显然不是你想看到的。
但如果使用了新的invokespecial ,他就会搜索类层次,找到最近的一个父类进行方法调用,得到正确的结果。第2中方法就是设置了ACC_SUPER时的情况。
现在编译器都会生成ACC_SUPER以支持一个正确的父类调用。