背景知识:java 字节码基本框架,jvm基本框架多态的机制。
Virtual Dispatch首先从字节码中对方法的调用说起。java的bytecode中对
方法的调用实现分为四种情况:1.invokevirtual 为最常见的情况,包含virtual dispatch机制; 2.invokespecial是作为private和构造方法的调用,绕过了virtual dispatch;3.invokeinterface的实现跟invokevirtual类似。4.invokestatic是对静态方法的调用。
其中最复杂的要属
invokevirtual . virtual dispatch机制会首先从
receiver(调用该方法的对象)的类的实现中查找对应的方法,如果没找到,则去父类查找,直到找到函数并实现调用,
而不是依赖于引用类型。下面是一段有趣的代码。反映了virtual dispatch机制 和 一般的field访问的不同。
- public class Greeting {
- String intro = "Hello";
- String target(){
- return "world";
- }
- }
- public class FrenchGreeting extends Greeting {
- String intro = "Bonjour";
- String target(){
- return "le monde";
- }
-
-
- public static void main(String[] args){
- Greeting english = new Greeting();
- Greeting french = new FrenchGreeting();
-
- System.out.println(english.intro + "," + english.target());
- System.out.println(french.intro + "," + french.target());
- System.out.println(((FrenchGreeting)french).intro + "," + ((FrenchGreeting)french).target());
- }
- }
运行的结果为
- Hello,world
- Hello,le monde
- Bonjour,le monde
其中的第二行是亮点。 对于intro这个filed的访问,直接指向了父类中的变量,因为
引用 类型为父类。而对于target的方法调用,则是指向了子类中的方法,虽然引用类型也为父类,但这是virtual dispatch的结果,
virtual dispatch不管引用类型的,只查receiver的类型。既然 虚分派 机制是从receiver对象的子类开始查找,由此看来,对于一个覆盖了父类中某方法的子类的对象,是无法调用父类中那个被覆盖的方法的吗?在虚分派机制中这确实是不可以的。但却可以通过invokespecial实现。如下代码
- public class FrenchGreeting extends Greeting {
- String intro = "Bonjour";
- String target(){
- return "le monde";
- }
-
- public String func(){
- return super.target();
- }
-
-
- public static void main(String[] args){
- Greeting english = new Greeting();
- FrenchGreeting french = new FrenchGreeting();
-
- System.out.println(french.func());
-
- }
- }
func()就成功的调用了父类的方法target,虽然target已经被子类重写了。怎么实现的?让我们看一看func方法中生成的字节码:
- ALOAD 0: this
- INVOKESPECIAL Greeting.target() : String
- ARETURN
原来如此,它是通过invokespecial 指令来调用的。