多态是java的基本特征之一,多态即一个对象具有多种形态(多种表达形式,猴子是动物的一种的表现形式),例如:子类是父类的一种形态。

当方法重载时,就会涉及到多态。

1:在重载时是通过参数的静态类型,而不是实际类型确定的, 静态类型是编译期确定的。

例如:



package com.li.chapter08;

/**
* 静态分派
*/
public class StaticDispatch {
static abstract class Human{
}

static class Women extends Human{
}

static class man extends Human{
}
public void sayHello(Human human) {
System.out.println("hello,human");
}

public void sayHello(Women women) {
System.out.println("hello, women");
}

public void sayHello(man man) {
System.out.println("hello, man");
}

public static void main(String[] args){
man man=new man(); //man是Human的一种形态,
Human women=new Women(); //Human是静态类型,Women是实际类型

StaticDispatch staticDispatch=new StaticDispatch();
staticDispatch.sayHello(man); //编译期确定参数的类型,使用 human的静态类型Human
staticDispatch.sayHello(women);

/**
* 结果
* hello,man
hello,human
*/
}
}


其中:Human 被称为静态类型,man,Women被称为实际类型。在编译期,参数就确定了,使用对象的静态类型。

2:当重载方法时,参数可能有很多版本,也就是参数对象具有层次结构。具体选择哪一种重置方法要看静态类型的匹配优先级

       例如;

       



package com.li.chapter08;

import org.junit.Test;

/**
* @program: GradleTestUseSubModule
* @author: Yafei Li
* @create: 2018-07-07 08:29
* 自定义方法重载,选择合适的重载方法
* 静态分派(Static Dispatch)发生在编译时期,分派根据静态类型(父类型)信息发生。静态分派对于我们来说并不陌生,方法重载就是静态分派。
* 动态单分派在Java语言中是在子类重写父类的方法时发生的
**/
public class MyStaticDispatcher {

class OneGeneration{
}
class TwoGeneration extends OneGeneration{

}
class ThreeGeneration extends TwoGeneration{

}
class FourGeneration extends ThreeGeneration{

}

public void test(TwoGeneration twoGeneration) {
System.out.println("twoGenration");
}

public void test(ThreeGeneration threeGeneration) {
System.out.println("threeGeneration");
}

@Test
public void main(){
MyStaticDispatcher myStaticDispatcher=new MyStaticDispatcher();
FourGeneration fourGeneration=new FourGeneration();
myStaticDispatcher.test(fourGeneration); //打印:threeGeneration

}
}


  当传入参数的对象,确定使用哪一种重载的方法时,需要根据参数对象在继承结构中从低到高遍历,选择第一个匹配的重载方法。

3:动态分派

      动态分派是多态性的另一个重要体现——(重写)override



package com.li.chapter08;

import org.apache.tools.ant.taskdefs.Java;

import java.lang.invoke.MethodType;

/**
* 动态分派
*/
public class DynamicDispatch {
static abstract class Human{
protected abstract void sayHello();
}
static class Man extends Human{

@Override //重写
protected void sayHello() {
System.out.println("man say hello");
}
}
static class Woman extends Human{

@Override //重写,覆盖
protected void sayHello() {
System.out.println("Woman say hello");
}
}

public static void main(String[] args){
Human man=new Man();
Human woman=new Woman();

man.sayHello();
woman.sayHello();

man=new Woman();
man.sayHello();
/**
* man say hello
Woman say hello
Woman say hello
*/
}
}


  使用 invokevirtual指令进行多态性的查找时,使用以下几个步骤:

​1)找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C。​

​2)如果在类型C中找到与常量中的描述符和简单名称都相符的方法,则进行访问权限校验,如果通过则返回这个方法的直接引用,查找过程结束;如果不通过,则返回java.lang.IllegalAccessError异常。​

​3)否则,按照继承关系从下往上依次对C的各个父类进行第2步的搜索和验证过程。​

​4)如果始终没有找到合适的方法,则抛出java.lang.AbstractMethodError异常。​

我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。

4:单分派与多分派

      方法的接受者(所有者)与方法的参数统称为方法的宗量,根据分配基于多少宗量,可以划分为单分派和多分派两种。

     单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择。

       java语言是动态单分派(只根据方法的接收者的实际类型确定),静态多分派(根据编译期静态类型和参数类型确定)语言。

 

5:访问者模式中用的动态多分派。


本博客为非营利性个人原创,除部分有明确署名的作品外,所刊登的所有作品的著作权均为本人所拥有,本人保留所有法定权利。违者必究