java方法调用之多态的补充示例(四)

前一篇博文 java方法调用之动态调用多态(重写override)的实现原理——方法表(三) 讲了多态的实现原理,这里写一个补充示例。

结论

方法表存放的只是invokevirtual和invokeinterface调用的方法,不包括invokestatic和invokespecial的静态方法、私有方法、构造器方法和父类方法,当然也不包括成员变量。

示例一

package org.fan.learn.polymorphism;

/** * Created by Administrator on 2016/3/31. */
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());
    }
}

注意这里引用了成员变量。
执行结果如下所示:

关键的字节码指令如下所示:

//english.intro对应的字节码指令
26: aload_1 //局部变量表的1号slot中存放的就是english 27: getfield #11; //Field Greeting.intro:Ljava/lang/String;
//english.target()
38: aload_1 39: invokevirtual #14; //Method Greeting.target:()Ljava/lang/String;
//french.intro 对应的字节码指令 
61: aload_2 //局部变量表的2号slot中存放的就是french 62: getfield #11; //Field Greeting.intro:Ljava/lang/String;
//french.target()
73: aload_2 74: invokevirtual #14; //Method Greeting.target:()Ljava/lang/String;
//((FrenchGreeting)french).intro
96: aload_2 97: checkcast #6; //class FrenchGreeting
100: getfield #3; //Field FrenchGreeting.intro:Ljava/lang/String;
//((FrenchGreeting)french).target()
111: aload_2 112: checkcast #6; //class FrenchGreeting
115: invokevirtual #17; //Method FrenchGreeting.target:()Ljava/lang/String;

这里只是给出了关于多态的字节码指令,前面的博文 由常量池 运行时常量池 String intern方法想到的(二)之class文件及字节码指令 中有讲到 String的+操作符都被优化成了StringBuilder。这里的System.out.println(english.intro + "," + english.target()); 中的+也被优化了。
从字节码中可以看出,english.introfrench.intro 的字节码指令是一样的; english.target()french.target() 的字节码指令也是一样的,调用的都是静态类型的成员变量和方法。但是经过强制类型转换之后,java编译器就会自动调用强制类型转换后的属性和方法。
也就是说:多态只适用于父子类同样签名的方法,而属性是不参与多态的。

示例二

package org.fan.learn.polymorphism;

/** * Created by Administrator on 2016/3/30. */



public class BaseChildTest {

    static class BaseClass {
        //注意这个是私有方法
        private void priMethod()
        {
            System.out.println("BaseClass scret");
        }

        public void print()
        {
            System.out.println("print_BaseClass");
        }

        public void baseMethod()
        {
            print();
            priMethod();
        }
    }
    static class ChildClass extends BaseClass {
    //注意这个是私有方法,没有override BaseClass中的方法
        private void priMethod()
        {
            System.out.println("ChildClass scret");
        }

        public void print()
        {
            System.out.println("print_ChildClass");
        }

        public void childMethod()
        {
            System.out.println("childMethod");
        }
    }

    public static void main(String[] args) {
        BaseClass bc = new ChildClass();
        bc.print();
        bc.baseMethod();
    }
}

注意这里面有私有方法,私有方法不参与继承, 也不会出现在方法表中,因为私有方法是由invokespecial指令调用的。
执行结果如下所示:
这里写图片描述
这个的字节码指令需要看两个文件。

javap -verbose -c BaseChildTest
javap -verbose -c BaseChildTest$ChildClass
javap -verbose -c BaseChildTest$BaseClass

先看Main方法:

public static void main(java.lang.String[]);
  Code: Stack=2, Locals=2, Args_size=1 0: new #2; //class BaseChildTest$ChildClass
   3: dup 4: invokespecial #3; //Method BaseChildTest$ChildClass."<init>":()V
   7: astore_1 8: aload_1 9: invokevirtual #4; //Method BaseChildTest$BaseClass.print:()V
   12: aload_1 13: invokevirtual #5; //Method BaseChildTest$BaseClass.baseMethod:()V
   16: return

从中可以看到调用的都是BaseClass中的方法。
看看BaseClass中的baseMethod方法:

public void baseMethod();
  Code:
   Stack=1, Locals=1, Args_size=1
   0:   aload_0
   1:   invokevirtual   #6; //Method print:()V
   4:   aload_0
   5:   invokespecial   #7; //Method priMethod:()V
   8:   return

可以看到print方法使用的是invokevirtual,而priMethod使用的是invokespecial。根据在bc.baseMethod();调用时传递的this指针,print方法会发生多态的选择,而priMethod不会有多态发生。

结束语

  1. 成员变量的访问只根据静态类型进行选择。
  2. 私有方法不会发生多态选择,只根据静态类型进选择。

你可能感兴趣的:(java,多态,示例)