前言:
学习java的同学都知道,java是一门面向对象的语言,而在基础课中,说的最多的就是面向对象的三个基本特征:继承,封装,多态。本次分派调用方法过程中java虚拟机是如何来实现分派的,主要是揭释多态的一些特征。当然我们所说的分派是比较常见的,例如:重载,重写。
阅读须知:
此次博客以启发性代码和解释进行学习。在阅读时,按照代码和提示进行思考分析为什么,请思考过后在看答案来验证自己的思考。(此博客为个人观点且本人水平有限,如有错,请批评指正)
小题测试:(先思考,并写出自己的答案后在往下看)
package com.wen.demo.test.MyClass;
/**
* 静态分配案例
* @author wen
*/
public class StaticDispatch {
/**
*
* @param human
*/
public void hello(Human human) {
System.out.println("呵·人类");
}
public void hello(WoMan woMan) {
System.out.println("呵·女人");
}
public void hello(Man man) {
System.out.println("呵·男人");
}
public static void main(String[] args) {
StaticDispatch staticDispatch = new StaticDispatch();
Human human = new WoMan();
Human human1 = new Man();
staticDispatch.hello(human);
staticDispatch.hello(human1);
}
}
/**
* 留着给继承
*/
abstract class Human {
}
class Man extends Human {
}
class WoMan extends Human {
}
代码不多,但是对于以上代码,请先思考,输出的是什么。然后才往下面看。
。
。
。
。
概念:
这次先说说 答案:
上面的代码是很考我们的基本功的,也是我们在学javase里是否基础扎实的体现方式之一。当然,面试题也会有这种类型,看似简单,基本不好或对jvm没有了解的,真的会吃大亏。今天就在这里认真撸一遍,给自己补补基础。
关注点我们应该下面这段代码:
Human human = new WoMan(); Human human1 = new Man();
官方一点的表达:
上面代码中“Human“称为变量的静态类型,而“WoMan”和“Man”我们称为变量的实际类型。
区别:
(1)静态类型的变化仅仅在使用时才发生,变量本身的静态类型是不会被改变,并且最终静态类型在编译期是可知的。
(2)实际类型的变化是在运行期才知道,编译器在编译程序时并不知道一个对象的具体类型是什么。
所以,编译器在重载时,通过参数的静态类型来作为判定用哪个方法的依据,而不是使用实际类型作为判定依据。
完了。理解不了呀,这书上说的太高端大气上档次了吧。怪我们这些苦逼的屌丝如何理解。别急,先看看下面的代码:
//实际类型变化 Human human = new WoMan(); human = new Man(); //静态类型变化一下 staticDispatch.hello( (WoMan) human); staticDispatch.hello( (Man) human); //编译器没有报错。。没有报错,所以说是可以通过编译器,但是在运行期就会报错了
错误:
看了上面的小提示,还是有点不懂额。
Human human = new WoMan();在想用哪个类型时,你声明的是什么类型,编译器在编译期时就是什么类型。比如刚刚我们声明的Human类作为我们将要调用的类型也就是hello(HuMan huMan),如果是其他就调用其他。所以,这就是为什么输出的结果是:呵丶人类的结果了。
简单表达:
声明什么类型编译器就以那个类型调用相关的方法。
比如:HuMan huMan = new Man(), 我们声明的是HuMan类型,那么编译器就调用方法是 hello(HuMan huMan) 。而不是根据其他类型。
总结:
所依据静态类型来定位方法执行的版本的分派就是我们今天所说的静态分派。静态分派的典型应该就是方法重载了。在以前的学习时,我们只需要了解重载和重写的区别。如今,我们需要了解的是静态分派发生在编译时期,,编译器可以确定我们调用的是哪个版本的重载,当然,这个静态分派不是唯一的,它也有执行的优先级,这个优先级我们下一篇在讲解。