前言
了解 行为方法分派 有利于在行为分派时时进行一些功能操作
本文全面讲解行为分派的类型: 静态 & 动态行为分派, 希望你们会喜欢.
在接下来的日子, 我会推出一系列讲解 JVM 的文章, 具体如下; 感兴趣可持续关注 Carson_Ho 的安卓开发笔记
示意图
目录
示意图
1. 知识储备
1.1 分派
定义: 确定执行哪个方法 的过程
a. 疑问
有些读者会问, 方法的执行不是取决于代码设置中的执行对象吗? 为什么还要选择呢?
b. 回答
若 一个对象对应于多个方法 时, 就需要进行选择了
读者应该都想到了 Java 中的特性: 多态, 即重写 & 重载. 下面我会详细讲解.
分类: 静态分派 & 动态分派. 下面我将详细讲解.
1.2 变量的静态类型 & 动态类型
先看下面的代码
- public class Test {
- static abstract class Human {
- }
- static class Man extends Human {
- }
- static class Woman extends Human {
- }
- // 执行代码
- public static void main(String[] args) {
- Human man = new Man();
- // 变量 man 的静态类型 = 引用类型 = Human: 不会被改变, 在编译器可知
- // 变量 man 的动态类型 = 实例对象类型 = Man: 会变化, 在运行期才可知
- }
- }
即:
变量的静态类型 = 引用类型 : 不会被改变, 在编译器可知
变量的动态类型 = 实例对象类型 : 会变化, 在运行期才可知
下面, 我将详细讲解 Java 中的分派类型: 静态分派 & 动态分派
2. 静态分派
定义
根据 变量的静态类型 进行方法分派 的 行为
即根据 变量的静态类型 确定执行哪个方法
发生在编译期, 所以不由 Java 虚拟机来执行
应用场景
方法重载(OverLoad)
实例说明
- public class Test {
- // 类定义
- static abstract class Human {
- }
- // 继承自抽象类 Human
- static class Man extends Human {
- }
- static class Woman extends Human {
- }
- // 可供重载的方法
- public void sayHello(Human guy) {
- System.out.println("hello,guy!");
- }
- public void sayHello(Man guy) {
- System.out.println("hello gentleman!");
- }
- public void sayHello(Woman guy) {
- System.out.println("hello lady!");
- }
- // 测试代码
- public static void main(String[] args) {
- Human man = new Man();
- Human woman = new Woman();
- Test test = new Test();
- test.sayHello(man);
- test.sayHello(woman);
- }
- }
- // 运行结果
- hello,guy!
- hello,guy!
根据上述的讲解, 大家应该明白运行结果的原因:
方法重载 (OverLoad) = 静态分派 = 根据 变量的静态类型 确定执行(重载) 哪个方法
所以上述的方法执行时, 是根据变量 (man,woman) 的静态类型 (Human) 确定重载 sayHello()中参数为 Human guy 的方法, 即 sayHello(Human guy)
特别注意
a. 变量的静态类型 发生变化 的情况
可通过 强制类型转换 改变 变量的静态类型
- Human man = new Man();
- test.sayHello((Man)man);
- // 强制类型转换
- // 此时 man 的静态类型从 Human 变为 Man
- // 所以会调用 sayHello()中参数为 Man guy 的方法, 即 sayHello(Man guy)
b. 静态分派的优先级匹配问题
问题描述:
背景
现需要进行静态分派
问题
程序中 没有显示指定 静态类型
解决方案
程序会根据 静态类型的优先级 从而选择 优先的静态类型进行方法分配.
实例说明
- public class Overload {
- private static void sayHello(char arg){
- System.out.println("hello char");
- }
- private static void sayHello(Object arg){
- System.out.println("hello Object");
- }
- private static void sayHello(int arg){
- System.out.println("hello int");
- }
- private static void sayHello(long arg){
- System.out.println("hello long");
- }
- // 测试代码
- public static void main(String[] args) {
- sayHello('a');
- }
- }
- // 运行结果
- hello char
因为'a'是一个 char 类型数据(即静态类型是 char), 所以会选择参数类型为 char 的重载方法.
若注释掉 sayHello(char arg)方法, 那么会输出
hello int
因为'a'除了可代表字符串, 还可代表数字 97. 因此当没有最合适的 sayHello(char arg)方式进行重载时, 会选择第二合适 (第二优先级) 的方法重载, 即
sayHello(int arg)
总结: 当没有最合适的方法进行重载时, 会选优先级第二高的的方法进行重载, 如此类推.
优先级顺序为:
char>int>long>float>double>Character>Serializable>Object>...
其中... 为变长参数, 将其视为一个数组元素. 变长参数的重载优先级最低.
因为 char 转型到 byte 或 short 的过程是不安全的, 所以不会选择参数类型为 byte 或 short 的方法进行重载, 故优先级列表里也没有.
特别注意
上面讲解的主要是 基本数据类型的优先级匹配问题
若是引用类型, 则根据 继承关系 进行优先级匹配
注意只跟其编译时类型 (即静态类型) 相关
3. 动态分派
定义
根据 变量的动态类型 进行方法分派 的 行为
即根据 变量的动态类型 确定执行哪个方法
应用场景
方法重写(Override)
实例说明
- // 定义类
- class Human {
- public void sayHello(){
- System.out.println("Human say hello");
- }
- }
- // 继承自 抽象类 Human 并 重写 sayHello()
- class Man extends Human {
- @Override
- protected void sayHello() {
- System.out.println("man say hello");
- }
- }
- class Woman extends Human {
- @Override
- protected void sayHello() {
- System.out.println("woman say hello");
- }
- }
- // 测试代码
- public static void main(String[] args) {
- // 情况 1
- Human man = new man();
- man.sayHello();
- // 情况 2
- man = new Woman();
- man.sayHello();
- }
- }
- // 运行结果
- man say hello
- woman say hello
- // 原因解析
- // 1. 方法重写 (Override) = 动态分派 = 根据 变量的动态类型 确定执行(重写) 哪个方法
- // 2. 对于情况 1: 根据变量 (Man) 的动态类型 (man) 确定调用 man 中的重写方法 sayHello()
- // 3. 对于情况 2: 根据变量 (Man) 的动态类型 (woman) 确定调用 woman 中的重写方法 sayHello()
特别注意
对于代码中:
- Human man = new Man();
- man = new Woman();
- man.sayHello();
- // man 称为执行 sayHello()方法的所有者, 即接受者.
invokevirtual 指令执行的第一步 = 确定接受者的实际类型
invokevirtual 指令执行的第二步 = 将 常量池中 类方法符号引用 解析到不同的直接引用上
第二步即方法重写 (Override) 的本质
4. 二者区别
示意图
5. 总结
本文全面讲解方法分派的类型 & 过程
在接下来的日子, 我会推出一系列讲解 JVM 的文章, 具体如下; 感兴趣可持续关注 Carson_Ho 的安卓开发笔记
示意图
请点赞! 因为你的鼓励是我写作的最大动力!
来源: http://www.jianshu.com/p/3ae6efff1c96