您的位置:首页 > 编程语言 > Java开发

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

2016-03-31 16:00 579 查看
前一篇博文 java方法调用之动态调用多态(重写override)的实现原理——方法表(三) 讲了多态的实现原理,这里写一个补充示例。

结论

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

示例一

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.intro
french.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不会有多态发生。

结束语

成员变量的访问只根据静态类型进行选择。

私有方法不会发生多态选择,只根据静态类型进选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java 多态 示例