您的位置:首页 > 其它

分派

2017-11-06 10:36 99 查看

静态分派和动态分派

静态分派案例:

public class fenpai {
static  abstract class A {

}
static class B extends A {

}
static class C extends A {

}

public static void sayHi(A a) {
System.out.println("hi ,father");
}
public void sayHi(B b) {
System.out.println("hi ,sister");
}
public void sayHi(C c) {
System.out.println("hi ,brother");
}

public static void main(String[] args) {
fenpai f = new  fenpai();
A a1 = new B();
A a2 = new C();
f.sayHi(a1);
f.sayHi(a2);
}
}


hi ,father
hi ,father


动态分派案例:

public class fenpai {
static  abstract class A {
public void sayHi() {
System.out.println("hi ,father");
}
}
static class B extends A {
@Override
public void sayHi() {
System.out.println("hi ,father");
}
}
static class C extends A {
@Override
public void sayHi() {
System.out.println("hi ,sister");
}
}

public void sayHi(C c) {
System.out.println("hi ,brother");
}

public static void main(String[] args) {
fenpai f = new  fenpai();
A a1 = new B();
A a2 = new C();
a1.sayHi();
a2.sayHi();
}
}


hi ,father
hi ,sister


解释一下两种结果不同的原因:

父类A称为变量的静态类型,也叫外观类型,子类BC称为实际类型,静态类型是编译期可知的,而实际类型是运行期才知道。

结果不同的原因是:

静态分配的虚拟机重载时,是通过参数的静态类型而不是实际类型进行判定的,编译器在编译阶段,直接根据静态类型决定使用哪个版本。

而动态分配是方法的重写,Java虚拟机是通过实际类型来判断执行的版本。

让我们来看一下他们的二进制字节码来了解编译器干了什么?

静态分配

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new           #1                  // class fenpai
3: dup
4: invokespecial #44                 // Method "<init>":()V
7: astore_1
8: new           #45                 // class fenpai$B
11: dup
12: invokespecial #47                 // Method fenpai$B."<init>":()V
15: astore_2
16: new           #48                 // class fenpai$C
19: dup
20: invokespecial #50                 // Method fenpai$C."<init>":()V
23: astore_3
24: aload_2
25: invokestatic  #51                 // Method sayHi:(Lfenpai$A;)V
28: aload_3
29: invokestatic  #51                 // Method sayHi:(Lfenpai$A;)V
32: return
LineNumberTable:
line 24: 0
line 25: 8
line 26: 16
line 27: 24
line 28: 28
line 29: 32
LocalVariableTa


动态分配

public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=4, args_size=1
0: new           #1                  // class fenpai
3: dup
4: invokespecial #34                 // Method "<init>":()V
7: astore_1
8: new           #35                 // class fenpai$B
11: dup
12: invokespecial #37                 // Method fenpai$B."<init>":()V
15: astore_2
16: new           #38                 // class fenpai$C
19: dup
20: invokespecial #40                 // Method fenpai$C."<init>":()V
23: astore_3
24: aload_2
25: invokevirtual #41                 // Method fenpai$A.sayHi:()V
28: aload_3
29: invokevirtual #41                 // Method fenpai$A.sayHi:()V
32: return
LineNumberTable:
line 28: 0
line 29: 8
line 30: 16
line 31: 24
line 32: 28
line 33: 32


很清楚地看到静态分配的25行 29行
invokestatic


动态分配的25行,29行
invokevirtual


这就是区别所在。

a)静态分配在编译器就决定了使用哪个重载版本,所以他把这个决定的重载方法写入了invokestatic指令中。

b)动态分配,编译器不知道实际类型,所以他不知道执行哪个重写版本,于是它只能写入invokevirtual指令中。

invokevirtual指令执行过程:

1)确定接受者的实际类型。

2)在操作数栈找到对应这个实际类型。

3)在类型中找到与常量中描述符合和简单名称都相同的方法,进行访问权限检验,如果通过,则返回这个方法的直接引用,如果不通过,返回java.lang.IllegalAccessError 异常。

4)否则,按照继承关系从上往下对这个实际类型的各个父类进行第三步的搜索和验证功能。

5)如果没有合适的方法,则抛出java.lang.AbstractMethodError。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息