目录:
- 函数解析
- 静态分派、动态分派
函数解析
1、虚拟机方法字节码:
Java符合编译期可知,运行期不可变的两类方法是:静态方法和私有方法。
与之对应的是5条方法调用的字节码指令:
- invokestatic:调用静态方法。
- invokespecial:调用实例构造器(<init>)方法、私有方法、父类方法。
- invokevirtual:调用虚方法。
- invokeinterface:调用接口方法,会在运行时确定一个实现。
- invokedynamic:运行时动态解析引用的方法,由用户所指定。
2、静态类型和实际类型:
1 Human human = new Pserson();
Human称为变量的静态类型(Static Type),或者叫做的外观类型(Apparent Type),后面的Man则称为变量的实际类型(Actual Type)。
3、虚方法:
非虚方法:能被invokestatic和invokespecial指令调用的方法,也就是可在解析阶段中确定唯一调用版本的方法,如静态方法、私有方法、示例构造器、父类方法这4类。
虚方法:除去final方法和非虚方法,其它都成为虚方法。
4、虚方法表:
方法的调用若是根据符号引用一个个执行的话,势必会影响性能,所以JVM增加了虚方法表这一概念来优化方法的调用。
虚方法表中存放着各个方法的实际入口地址,一般是在类加载的阶段进行初始化(虚方法表存在于方法区)。
- 如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口。
- 如果子类中重写了这个方法,子类方法表中的地址将会替换为指向子类实现版本的入口地址 。
静态分派
所有依赖静态类型来定位方法执行版本的分派动作称为静态分派,静态分派的典型应用是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。
编译器在重载时是通过参数的静态类型而不是实际类型作为判定依据的,在编译阶段,Javac编译器会根据参数的静态类型决定使用哪个重载版本。
1 public class StaticDispatch { 2 3 static abstract class Human { 4 } 5 6 static class Man extends Human { 7 } 8 9 static class Woman extends Human { 10 } 11 12 public void sayHello(Human guy) { 13 System.out.println("hello,guy!"); 14 } 15 16 public void sayHello(Man guy) { 17 System.out.println("hello,gentleman!"); 18 } 19 20 public void sayHello(Woman guy) { 21 System.out.println("hello,lady!"); 22 } 23 24 public static void main(String[] args) { 25 Human man = new Man(); 26 Human woman = new Woman(); 27 StaticDispatch sr = new StaticDispatch(); 28 // 结果:hello,guy! 29 sr.sayHello(man); 30 // 结果:hello,guy! 31 sr.sayHello(woman); 32 } 33 34 }
因为重载是根据静态类型判定的,所以结果都指向public void sayHello(Human guy)。
动态分派
不是根据静态类型来确定,而是在运行时根据实际类型来决定函数的版本,如多态。
1 public class DynamicDispatch { 2 3 static class Human { 4 protected void sayHello() { 5 System.out.println("Human"); 6 } 7 } 8 9 static class Man extends Human { 10 @Override 11 protected void sayHello() { 12 System.out.println("man say hello"); 13 } 14 } 15 16 static class Woman extends Human { 17 @Override 18 protected void sayHello() { 19 System.out.println("woman say hello"); 20 } 21 } 22 23 public static void main(String[] args) { 24 // 动态分派:不是根据静态类型来确定,而是在运行时根据实际类型来决定函数的版本。 25 Human man = new Man(); 26 Human woman = new Woman(); 27 man.sayHello(); 28 woman.sayHello(); 29 man = new Woman(); 30 man.sayHello(); 31 } 32 33 }
综上:Java语言是一门静态多分派、 动态单分派的语言。