标签:
从字节码层面来看,Java中的所有方法调用,最终无外乎转换为如下几条调用指令。
JVM提供了上述5条方法调用指令,所以不妨从字节码层面来一窥Java多态机制的执行过程。
上述5条方法调用指令中的invokevirtual负责调用所有的虚方法。那么什么是虚方法?什么是非虚方法呢?
从Java语言层面来看,static,private,final修饰的方法,父类方法以及实例构造器,这些方法称为非虚方法。与之相反,其他所有的方法称为虚方法。
字节码指令层面来讲,invokestatic和invokespecial调用的方法都是非虚方法。
先看一看以下代码的定义:
Human man = new Man();
我们把 Human称为变量的静态类型,Man称为变量的实际类型。
引用变量都有两个类型:静态类型(定义该变量的类型)和实际类型(实际指向的对象的类型)。
静态类型是编译时可知的,而实际类型只有运行时才能确定。
invokevirtual指令的运行时解析过程大致分为如下几个步骤:
正是由于invokevirtual指令是这样的一个执行过程,所以这就解释了为什么java语言里面实现多态需要如下三个条件:a. 父类引用指向子类对象。b. 有继承的存在。c. 子类重写父类方法。
分派过程会揭示多态的一些最基本的体现,比如”重写“和“重载”在Java虚拟机中是如何实现的。
所有依赖(实参的)静态类型来定位方法执行版本的分派动作称为静态分派。典型的应用就是方法重载(Overload)。
先看一个例子:
public class StaticDispatch {
static class Human {
}
static class Man extends Human {
}
static class Women extends Human {
}
public void sayHello(Human guy) {
System.out.println("hello, guy!");
}
public void sayHello(Man guy) {
System.out.println("hello, man!");
}
public void sayHello(Women guy) {
System.out.println("hello, women!");
}
public static void main(String[] args) {
Human man = new Man();
Human women = new Women();
StaticDispatch sd = new StaticDispatch();
sd.sayHello(man);
sd.sayHello(women);
}
}
输出结果:
hello, guy!
hello, guy!
没错,程序就是大家熟悉的重载(Overload)。在上述程序中,由于方法的接受者已经确定是StaticDispatch的实例sd了,所以最终调用的是哪个重载版本也就取决于传入参数的类型了。
编译器在重载时是根据传入实参的静态类型而不是实际类型作为判定依据的。静态类型是编译时可知的,所以在编译阶段,编译器会根据实参的静态类型决定调用那个重载版本。
在运行期根据变量的实际类型来确定方法执行版本的分派过错称为动态分派。典型的应用就是重写(override)。例子如下:
public class DynamicDispatch { public static void main(String[] args) { Human man = new Man(); Human women = new Women(); man.sayHello(); women.sayHello(); man = new Women(); man.sayHello(); } } abstract class Human { protected abstract void sayHello(); } class Man extends Human { @Override protected void sayHello() { System.out.println("hello man!"); } } class Women extends Human { @Override protected void sayHello() { System.out.println("hello women!"); } }
输出结果:
hello man!
hello women!
hello women!
见invokevirtual指令的运行时的解析过程。
public class Dispatch {
static class QQ{}
static class _360{}
public static class Father{
public void hardChoice(_360 _360) {
System.out.println("Father choose 360");
}
public void hardChoice(QQ qq) {
System.out.println("Father choose qq");
}
}
public static class Son extends Father{
public void hardChoice(_360 _360) {
System.out.println("Son choose 360");
}
public void hardChoice(QQ qq) {
System.out.println("Son choose qq");
}
}
public static void main(String[] args) {
Father father = new Father();
Father son = new Son();
father.hardChoice(new _360());
son.hardChoice(new QQ());
}
}
输出结果:
Father choose 360
Son choose qq
分析如下:
Father father = new Father();
Father son = new Son();
/**
* Father里面有两个重载的hardChoice方法。所以会根据hardChoice()的实参的【静态类型】来决定调用哪个版本的方法。
*/
father.hardChoice(new _360());
/**
* 变量son的静态类型是Father,实际类型是Son。并且类Son重写了父类Father里面的两个重载的hardChoice方法。
* 所以运行的时候首先会确定调用子类Son里面的方法,然后在根据hardChoice()的实参的【静态类型】来决定调用Son里面的哪个版本的方法。
*/
son.hardChoice(new QQ());
http://blog.csdn.net/kobejayandy/article/details/39620679
http://www.cnblogs.com/jack204/archive/2012/10/29/2745150.html
http://blog.csdn.net/oypc2303/article/details/4393831
http://blog.csdn.net/lidaweihgy/article/details/7660346
http://www.cnblogs.com/qinqinmeiren/archive/2011/07/15/2151687.html
http://www.geekcome.com/content-10-4128-1.html
标签:
原文地址:http://www.cnblogs.com/michaellau/p/4415628.html