首页 > 编程语言 >Java语言中多态的机制

Java语言中多态的机制

时间:2022-10-09 15:58:41浏览次数:54  
标签:调用 Java 语言 静态 多态 分派 编译 类型 方法

1. 方法解析

Class文件的编译过程中,不包含传统编译中的连接步骤,一切方法的调用在Class文件中存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址

这个特性给java带来了更强大的动态扩展能力,使得可以在类运行期间才能确定某些目标方法的直接引用,称为动态连接

也有一部分方法的符号引用在类加载阶段或第一次使用时转化为直接引用,称为静态解析

静态解析成立的条件是:方法在程序真正执行前,就有一个可以确定调用的版本,并且这个方法的调用版本在运行期是不可变的。换句话说,调用目标在编译器进行编译时就必须确定下来。这类方法的调用称为解析


在Java语言中:符合“编译器可知,运行期不可变”这个条件的方法主要有静态方法私有方法两大类。前者直接关联类型,后者在外部不可被访问。因此均不会出现通过继承或别的方式重写出其他版本的情况,从而都适合在类加载阶段进行解析。

Java虚拟机中,提供了4中方法调用字节指令,分别是

  1. invokestatic:调用静态方法
  2. invokespecial:调用实例构造器init方法、私有方法、父类方法。
  3. invokevirtual:调用所有的虚方法
  4. invokeinterface:调用接口方法,会在运行时确定一份实现此接口的对象

对于被1.2.两个指令调用的方法,都可以在解析阶段确定唯一的调用版本,符合这个条件的有静态方法、私有方法、实例构造器和父类方法四类。它们在类加载时就会把符号引用解析为该方法的直接引用。这些方法称为非虚方法。非虚方法还包括被final修饰的方法。

对于final修饰的方法,虽然调用final方法使用的是invervirtual指令,但是由于被final修饰的方法无法被覆盖,不会存在其他版本,所以也无需对方法接收者进行多态选择。

Java语言中,也明确规定了被fianl修饰的方法是一种非虚方法。

解析调用,一定是一个静态过程。在编译期间就会完全确定,在类加载的解析阶段就会把涉及的符号引用转化为可以确定的直接引用,不会延迟到运行期再去完成。

而分派调用则可能既是动态的,也可能是静态的。根据分派一句的宗量数,也可以分为单分派和多分派。两类分派方式两两结合便构成了静态单分派、静态多分派、动态单分派、动态多分派四种分派情况。


2. 静态分派

所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派。

静态分派的最典型的应用就是多态性中的方法重载。

静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的。

Human man = new Man();

对于以上代码,Human称为变量的静态类型,后面的Man称为变量的实际类型。静态类型和实际类型在程序中都可以发生一些变化,区别是静态类型的变化仅仅在使用时发生,变量本身的静态类型不会被改变,并且最终的静态类型是在编译器可知的,而实际类型变化的结果在运行期才可确定。

在调用方法时,方法的调用者为同一个静态类型时,调用哪一个重载版本完全取决于传入参数的数量和数据类型。编译器(不是虚拟机,因为根据静态类型做出判断,那么在编译期间就确定了)在重载时是通过参数的静态类型而不是实际类型作为判断依据的。并且,静态类型在编译期间是可知的,所以在编译阶段,javac编译器就根据参数的静态类型做出选择,决定使用哪一个重载版本。这就是静态分派的最典型应用。


3. 动态分派

动态分派与多态性的另一个重要体现——方法覆写由很紧密的联系。

向上转型调用子类的覆写方法便是一个很好的动态分派的例子。这种情况很常见,显然在判断执行父类中的方法还是子类中覆盖的方法时,如果用静态类型来判断,那么不论怎么进行向上转型,都只会调用父类中的方法。但实际情况是,根据父类实例化的子类不同,会调用不同子类的覆写方法,这是需要根据变量的实际类型来分派方法的执行版本的。

而实际类型的确定需要在程序运行时才能确定下来,这种在运行期根据实际类型确定方法执行版本的过程分派称为动态分派


4. 单分派和多分派

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

方法的接受者(即方法的调用者)于方法的参数统称为方法的宗量。

在编译期间,编译器的选择过程,即静态分派过程。这时选择目标方法的依据有两点:

  • 方法的接受者(调用者)的静态类型
  • 方法参数类型

因此是根据两个宗量来进行选择的。所以Java语言的静态分派属于多分派类型

在运行阶段,虚拟机选择的过程,即动态分派过程。这时候由于编译期已经确定了目标方法的参数类型(编译期根据参数的静态类型进行静态分派),因此唯一可以影响到虚拟机选择的因素只有一个:

  • 此方法的接受者(调用者)的实际类型

因为是根据一个宗量来进行选择的。所以Java语言的动态分派属于单分派类型


5. 总结

Java语言的多态实现机制:

  • 方法重载
    • 在编译期间,由编译器执行的静态分派,属于多分派类型,分派宗量有:
      • 方法接受者(调用者)的静态类型
      • 方法参数列表
  • 方法重写(方法覆写)
    • 在运行期间,由虚拟机执行的动态分派,属于单分派类型,分派宗量为:
      • 方法接受者(调用者)的实际类型。

标签:调用,Java,语言,静态,多态,分派,编译,类型,方法
From: https://www.cnblogs.com/radish40/p/16772399.html

相关文章

  • 【Java复健指南03】递归思想
    【递归】递归重要规则1.执行一个方法时,就创建一个新的受保护的独立空间(栈空间)方法的局部变量是独立的,不会相互影响,比如n变量如果方法中使用的是引用类型变量(比......
  • java序列化
    一、序列化与反序列化序列化:指堆内存中的java对象数据,通过某种方式把对存储到磁盘文件中,或者传递给其他网络节点(网络传输)。这个过程称为序列化,通常是指将数据结构或对象转......
  • 003Java的诞生
    003Java的诞生1、计算机语言发展史(1)第一代语言机器语言我们都知道计算机的基本计算方式都是基于二进制的方式。二进制:010111001010110010110100这种代码是直接输......
  • Java虚拟机详解(五)------JVM参数
    JVM参数有很多,其实我们直接使用默认的JVM参数,不去修改都可以满足大多数情况。但是如果你想在有限的硬件资源下,部署的系统达到最大的运行效率,那么进行相关的JVM参数设置是必......
  • 【Java复健指南01】简介与数组
    写在最前学习Java已经是很久之前的事情了,因为技术栈的转变,很久没有使用Java正经地开发过项目。对于该语言的理解也是停留在表面,因此萌生了重新学习的念头。一方面是为刷......
  • Java实现多线程
    Java实现多线程的方式有4种分别是继承Thread类,实现Runnable,Callable接口和通过线程池提交线程任务。其中实现Callable接口的方式可以获取返回值。1.继承Thread类通过继......
  • 浏览器中javascript简易实现json数据保存到客户端
    思路很简单,就是利用Blob、URL.createObjectURL()方法和<a>便签的HTML5新属性download来模拟远端文件下载保存。下面直接上代码savePath:function(){varme......
  • JavaScript异步概念及与c#异步的区别
    JS的异步操作函数往往是通过回调函数来实现异步任务的结果处理,在ES6之前如setTimeout函数和异步AJAX编程;在ES6规范后Promise类对象使得书写异步任务更加容易,返回Promise......
  • java---了解以下运算符
    了解即可1&2用于条件判断,&条件1和2都执行1&&2,条件1判断错误的情况下,条件2不执行&当运算符的化,例如4&7,两者上下对比都是1则为1,反之为0,结果就是二进制100也就是......
  • JAVA中计算两个日期时间的差值竟然也有这么多门道
    JAVA中计算两个日期时间的差值竟然也有这么多门道上半年春招的时候,作为面试官,对于面试表现的不错的同学会要求其写一小段代码看看。题目很简单:给定一个日期,然后计算下......