1.重载(overload)方法
对重载方法的调用主要看静态类型,静态类型是什么类型,就调用什么类型的参数方法。
2.重写(override)方法
对重写方法的调用主要看实际类型。实际类型如果实现了该方法则直接调用该方法,如果没有实现,则在继承关系中从低到高搜索有无实现。
3.
java文件的编译过程中不存在传统编译的连接过程,一切方法调用在class文件中存放的只是符号引用,而不是方法在实际运行时内存布局中的入口地址。
基本概念
1.静态类型与实际类型,方法接收者
1 Human man = new Man();
2 man.foo();
上面这条语句中,man的静态类型为Human,实际类型为Man。所谓方法接收者,就是指将要执行foo()方法的所有者(在多态中,有可能是父类Human的对象,也可能是子类Man的对象)。
2.字节码的方法调用指令
(1)invokestatic:调用静态方法
(2)invokespecial:调用实例构造器方法,私有方法和父类方法。
(3)invokevirtual:调用所有的虚方法。
(4)invokeinterface:调用接口方法,会在运行时再确定一个实现此接口的对象。
(5)invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法。
前2条指令(invokestatic, invokespecial),在类加载时就能把符号引用解析为直接引用,符合这个条件的有静态方法、实例构造器方法、私有方法、父类方法这4类,这4类方法叫非虚方法。
非虚方法除了上面静态方法、实例构造器方法、私有方法、父类方法这4种方法之外,还包括final方法。虽然final方法使用invokevirtual指令来调用,但是final方法无法被覆盖,没有其他版本,无需对方法接收者进行多态选择,或者说多态选择的结果是唯一的。
重载overload
重载只会发生在编译期,即编译器时jvm可以通过静态类型确定符号引用所对应的直接引用。
上面说的静态类型和动态类型都是可以变化的。静态类型发生变化(强制类型转换)时,对于编译器是可知的,即编译器知道对象的最终静态类型。而实际类型变化(对象指向了其他对象)时,编译器是不可知的,只有在运行时才可知。
1 //静态类型变化
2 sr.sayHello((Man) man);
3 sr.sayHello((Woman) man);
4 //实际类型变化
5 Human man = new Man();
6 man = new Woman();
重载只涉及静态类型的选择。
测试代码如下:
1 /**
2 * Created by fan on 2016/3/28.
3 */
4 public class StaticDispatcher {
5
6 static class Human {}
7 static class Man extends Human {}
8 static class Woman extends Human {}
9
10 public void sayHello(Human human) {
11 System.out.println("Hello guy!");
12 }
13
14 public void sayHello(Man man) {
15 System.out.println("Hello man!");
16 }
17
18 public void sayHello(Woman woman) {
19 System.out.println("Hello woman!");
20 }
21
22 public static void main(String[] args) {
23 StaticDispatcher staticDispatcher = new StaticDispatcher();
24 Human man = new Man();
25 Human woman = new Woman();
26 staticDispatcher.sayHello(man);
27 staticDispatcher.sayHello(woman);
28 staticDispatcher.sayHello((Man)man);
29 staticDispatcher.sayHello((Woman)man);
30 }
31 }
先看看执行结果:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iMjhDNwIjYzcjN4gTZwAzM3UTO2EGZiVWZxYWZ3QmY48CX2AzLchDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL5M3Lc9CX6MHc0RHaiojIsJye.png)
由此可见,当静态类型发生变化时,会调用相应类型的方法。但是,当将Man强制类型转换成Woman时,没有编译错误,却有运行时异常。“classCastException”类映射异常。
看看字节码指令:
javap -verbose -c StaticDispatcher
1 public static void main(java.lang.String[]);
2 Code:
3 Stack=2, Locals=4, Args_size=1
4 0: new #7; //class StaticDispatcher
5 3: dup
6 4: invokespecial #8; //Method "<init>":()V
7 7: astore_1
8 8: new #9; //class StaticDispatcher$Man
9 11: dup
10 12: invokespecial #10; //Method StaticDispatcher$Man."<init>":()V
11 15: astore_2
12 16: new #11; //class StaticDispatcher$Woman
13 19: dup
14 20: invokespecial #12; //Method StaticDispatcher$Woman."<init>":()V
15 23: astore_3
16 24: aload_1
17 25: aload_2
18 26: invokevirtual #13; //Method sayHello:(LStaticDispatcher$Human;)V
19 29: aload_1
20 30: aload_3
21 31: invokevirtual #13; //Method sayHello:(LStaticDispatcher$Human;)V
22 34: aload_1
23 35: aload_2
24 36: checkcast #9; //class StaticDispatcher$Man
25 39: invokevirtual #14; //Method sayHello:(LStaticDispatcher$Man;)V
26 42: aload_1
27 43: aload_2
28 44: checkcast #11; //class StaticDispatcher$Woman
29 47: invokevirtual #15; //Method sayHello:(LStaticDispatcher$Woman;)V
30 50: return
看到,在强制类型转换时,会有指令checkCast的调用,而且invokevirtual指令的调用方法也发生了变化
39: invokevirtual #14; //Method sayHello:(LStaticDispatcher$Man;)V
。
虚拟机(准确说是编译器)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。
对于字面量类型,编译器会自动进行类型转换。转换的顺序为:
char-int-long-float-double-Character-Serializable-Object
转换成Character是因为发生了自动装箱,转换成Serializable是因为Character实现了Serializable接口。
重写override
重写发生在运行期,在运行时jvm会先判断对象的动态类型,而后根据对象的动态类型选择对应vtable,从而根据符号引用找到对应的直接引用。
如:
BaseClass c = new ChildClass();
则c能访问的函数列表为Method1,Method2,即黄色部分。
1 /**
2 * Created by fan on 2016/3/29.
3 */
4 public class DynamicDispatcher {
5
6 static abstract class Human {
7 protected abstract void sayHello();
8 }
9
10 static class Man extends Human {
11
12 @Override
13 protected void sayHello() {
14 System.out.println("Man say hello");
15 }
16 }
17
18 static class Woman extends Human {
19
20 @Override
21 protected void sayHello() {
22 System.out.println("Woman say hello");
23 }
24 }
25
26 public static void main(String[] args) {
27 Human man = new Man();
28 Human woman = new Woman();
29 man.sayHello();
30 woman.sayHello();
31 man = new Woman();
32 man.sayHello();
33 }
34
35 }
执行结果:
看下字节码指令:
1 public static void main(java.lang.String[]);
2 Code:
3 Stack=2, Locals=3, Args_size=1
4 0: new #2; //class DynamicDispatcher$Man
5 3: dup
6 4: invokespecial #3; //Method DynamicDispatcher$Man."<init>":()V
7 7: astore_1
8 8: new #4; //class DynamicDispatcher$Woman
9 11: dup
10 12: invokespecial #5; //Method DynamicDispatcher$Woman."<init>":()V
11 15: astore_2
12 16: aload_1
13 17: invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V
14 20: aload_2
15 21: invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V
16 24: new #4; //class DynamicDispatcher$Woman
17 27: dup
18 28: invokespecial #5; //Method DynamicDispatcher$Woman."<init>":()V
19 31: astore_1
20 32: aload_1
21 33: invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V
22 36: return
从字节码中可以看到,他们调用的都是相同的方法
invokevirtual #6; //Method DynamicDispatcher$Human.sayHello:()V
,但是执行的结果却显示调用了不同的方法。因为,在编译阶段,编译器只知道对象的静态类型,而不知道实际类型,所以在class文件中只能确定要调用父类的方法。但是在执行时却会判断对象的实际类型。如果实际类型实现这个方法,则直接调用,如果没有实现,则按照继承关系从下往上一次检索,只要检索到就调用,如果始终没有检索到,则抛异常(难道能编译通过)。
(1)测试代码如下:
1 /**
2 * Created by fan on 2016/3/29.
3 */
4 public class Test {
5
6 static class Human {
7 protected void sayHello() {
8 System.out.println("Human say hello");
9 }
10 protected void sayHehe() {
11 System.out.println("Human say hehe");
12 }
13 }
14
15 static class Man extends Human {
16
17 @Override
18 protected void sayHello() {
19 System.out.println("Man say hello");
20 }
21
22 // protected void sayHehe() {
23 // System.out.println("Man say hehe");
24 // }
25 }
26
27 static class Woman extends Human {
28
29 @Override
30 protected void sayHello() {
31 System.out.println("Woman say hello");
32 }
33
34 // protected void sayHehe() {
35 // System.out.println("Woman say hehe");
36 // }
37 }
38
39 public static void main(String[] args) {
40 Human man = new Man();
41 man.sayHehe();
42 }
43
44 }
测试结果如下:
字节码指令:
1 public static void main(java.lang.String[]);
2 Code:
3 Stack=2, Locals=2, Args_size=1
4 0: new #2; //class Test$Man
5 3: dup
6 4: invokespecial #3; //Method Test$Man."<init>":()V
7 7: astore_1
8 8: aload_1
9 9: invokevirtual #4; //Method Test$Human.sayHehe:()V
10 12: return
字节码指令与上面代码的字节码指令没有本质区别。
(2)测试代码如下:
1 /**
2 * Created by fan on 2016/3/29.
3 */
4 public class Test {
5
6 static class Human {
7 protected void sayHello() {
8 }
9 }
10
11 static class Man extends Human {
12
13 @Override
14 protected void sayHello() {
15 System.out.println("Man say hello");
16 }
17
18 protected void sayHehe() {
19 System.out.println("Man say hehe");
20 }
21 }
22
23 static class Woman extends Human {
24
25 @Override
26 protected void sayHello() {
27 System.out.println("Woman say hello");
28 }
29
30 protected void sayHehe() {
31 System.out.println("Woman say hehe");
32 }
33 }
34
35 public static void main(String[] args) {
36 Human man = new Man();
37 man.sayHehe();
38 }
39
40 }
编译时报错:
这个例子说明了:Java编译器是基于静态类型进行检查的。
修改上面错误代码,如下所示:
1 /**
2 * Created by fan on 2016/3/29.
3 */
4 public class Test {
5
6 static class Human {
7 protected void sayHello() {
8 System.out.println("Human say hello");
9 }
10 // protected void sayHehe() {
11 // System.out.println("Human say hehe");
12 // }
13 }
14
15 static class Man extends Human {
16
17 @Override
18 protected void sayHello() {
19 System.out.println("Man say hello");
20 }
21
22 protected void sayHehe() {
23 System.out.println("Man say hehe");
24 }
25 }
26
27 static class Woman extends Human {
28
29 @Override
30 protected void sayHello() {
31 System.out.println("Woman say hello");
32 }
33
34 protected void sayHehe() {
35 System.out.println("Woman say hehe");
36 }
37 }
38
39 public static void main(String[] args) {
40 Man man = new Man();
41 man.sayHehe();
42 }
43
44 }
注意在Main方法中,改成了
Man man = new Man();
执行结果如下所示:
字节码指令如下所示:
1 public static void main(java.lang.String[]);
2 Code:
3 Stack=2, Locals=2, Args_size=1
4 0: new #2; //class Test$Man
5 3: dup
6 4: invokespecial #3; //Method Test$Man."<init>":()V
7 7: astore_1
8 8: aload_1
9 9: invokevirtual #4; //Method Test$Man.sayHehe:()V
10 12: return
注意上面的字节码指令
invokevirtual #4; //Method Test$Man.sayHehe:()V
。