一、静态分派
所有依赖静态类型来定位方法执行版本分派动作称为静态分派,静态分派的典型应用就是方法的重载。方法的重载是因为我们的方法名参数相同,但是方法的参数类型不同或者是参数的个数不同或者是参数位置不同。当我们的方法出现重载时,JVM是如何确定执行哪一个方法的呢,确定方法的依据又是什么?
现在给出结论,下面的两个结论对与对象和字面量都是管用.
- JVM 是根据引用变量类型来重载匹配方法;具体详情看1.1
- 如果方法中不存在对应的实际类型,那么就会进行向上转型匹配方法;具体详情看1.2
1.1、JVM是根据引用变量的类型来匹配重载方法的
package JVM;
import org.junit.Test;
public class OverloadTest {
@Test
public void test1(){
Human man = new Woman();
Human woman = new Woman();
OverloadTest overloadTest = new OverloadTest();
overloadTest.sayHello(man); //输出为Human Say Hello!
overloadTest.sayHello(woman); //输出为Human Say Hello!
}
@Test
public void test2(){
Man man = new Man();
Woman woman = new Woman();
OverloadTest overloadTest = new OverloadTest();
overloadTest.sayHello(man); //输出为Man Say Hello!
overloadTest.sayHello(woman); //输出为Woman Say Hello!
}
public void sayHello(Human human){
System.out.println("Human Say Hello!");
}
public void sayHello(Man man){
System.out.println("Man Say Hello!");
}
public void sayHello(Woman woman){
System.out.println("Woman Say Hello!");
}
}
abstract class Human{}
class Man extends Human{}
class Woman extends Human{}
1.2、 实际类型向上转型匹配方法
如果我们的引用变量的类型在重载方法不存在时,此时就会根据其父类来分派重载方法
package JVM;
import org.junit.Test;
public class OverloadTest {
@Test
public void test2(){
Man man = new Man();
Woman woman = new Woman();
OverloadTest overloadTest = new OverloadTest();
overloadTest.sayHello(man); //输出Human Say Hello!
overloadTest.sayHello(woman); //输出Human Say Hello!
}
public void sayHello(Human human){
System.out.println("Human Say Hello!");
}
}
abstract class Human{}
class Man extends Human{}
class Woman extends Human{}
二、动态分派
动态分派是由Java的特性继承和多态特性导致的,当一个父类引用子类对象的时候,然后调用方法时就会现二义性。那么虚拟机会用什么方法解决这个问题呢?与动态分派相关的就是方法重写。
动态分派的方法版本选择过程需要运行在类的方法元数据中搜索合适的方法。基于此,JVM为类在方法区中建立一个虚方法表。虚方法表中存放着各个方法的实际入口地址。如果方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类的虚方法表的地址入口相同,都指向父类的实现入口。方法表在类解析阶段就会初始化,在类准备阶段后,也就是在解析阶段会将方法表初始化完毕。上面的这段话就是下这个结论:
- 超类的引用变量引用子类对象,那么运行时对象调用方法就会调用实际类型的对象,如果实际类型重写了方法,那么调用实际类型的变量,否则就调用引用类型对象
2.1子类覆写了父类方法
package JVM;
public class OverrideTest {
public static void main(String[] args){
Animal animal = new Cat();
animal.shout();//输出:调用Animal的shout方法
}
}
class Animal{
public void shout(){
System.out.println("调用Animal的shout方法");
}
}
class Cat extends Animal{
@Override
public void shout(){
System.out.println("调用了Cat的shout方法");
}
}
2.2、子类没有覆写父类方法
package JVM;
public class OverrideTest {
public static void main(String[] args){
Animal animal = new Cat();
animal.shout();//输出:调用Animal的shout方法
}
}
class Animal{
public void shout(){
System.out.println("调用Animal的shout方法");
}
}
class Cat extends Animal{}
上面两个示例正好说明了我们的上面的结论
三、 静态分派和动态分派结合
Java是一门动态单分派,是一个静态多分派的语言。
package JVM;
public class OverrideAndOverload {
public static void main(String[] args){
Father1 man = new Son1();
Mail mail = new QQMail();
QQMail QQMail = new QQMail();
man.sendEmail(mail);
man.sendEmail(QQMail);
}
}
//Father1类和其子类Son1类
class Father1{
public void sendEmail(Mail mail){
System.out.println("Father 使用 Mail 发送邮件");
}
public void sendEmail(QQMail mail){
System.out.println("Father1 使用 QQMail 发送邮件");
}
public void sendEmail(WangyiMail mail){
System.out.println("Father1 使用 WangyiMail 发送邮件");
}
}
class Son1 extends Father1{
@Override
public void sendEmail(Mail mail){
System.out.println("Son1 使用 Mail 发送邮件");
}
@Override
public void sendEmail(QQMail mail){
System.out.println("Son1 使用 QQMail 发送邮件");
}
@Override
public void sendEmail(WangyiMail mail){
System.out.println("Son1 使用 WangyiMail 发送邮件");
}
}
//Mail类及其子类
abstract class Mail{}
class QQMail extends Mail{}
class WangyiMail extends Mail{}
输出结果
Son1 使用 Mail 发送邮件
Son1 使用 QQMail 发送邮件