返回顶部
首页 > 资讯 > 前端开发 > VUE >web开发中的方法调用是怎样的
  • 297
分享到

web开发中的方法调用是怎样的

2024-04-02 19:04:59 297人浏览 八月长安
摘要

这篇文章将为大家详细讲解有关web开发中的方法调用是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。方法调用是不是很熟悉?

这篇文章将为大家详细讲解有关web开发中的方法调用是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。

方法调用是不是很熟悉?那你真的了解它吗?今天就让我们来盘一下它。

首先大家要明确一个概念,此处的方法调用并不是方法中的代码被执行,而是要确定被调用方法的版本,即最终会调用哪一个方法。

解析

我们之前说过在类加载的解析阶段,会将一部分的符号引用转化为直接引用,该解析成立的前提是:方法在程序真正运行之前就已经有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。我们把这类方法的调用称为解析(Resolution)。

看到这个前提条件,有没有小伙伴联想到对象的多态性?图片没错,就是这样,在java中能满足不被重写的方法有静态方法、私有方法(不能被外部访问)、实例构造器和被final修饰的方法,因此它们都适合在类加载阶段进行解析,另外通过this或者super调用的父类方法也是在类加载阶段进行解析的。

指令集

调用不同类型的方法,字节码指令集里设置了不同的指令,在JVM里面提供了5条方法调用字节码指令:

  • invokestatic:调用静态方法,解析阶段确定唯一方法版本

  • invokespecial:实例构造器init方法、私有及父类方法,解析阶段确定唯一方法版本

  • invokevirtual:调用所有虚方法

  • invokeinterface:调用接口方法,在运行时再确定一个实现该接口的对象

invokedynamic:先在运行时动态解析出调用点限定符所引用的方法,然后再执行该方法,在此之前的4条调用指令,分派逻辑是固化在Java虚拟机内部的,而invokedynamic指令的分派逻辑是由用户所设定的引导方法决定的。

invokedynamic指令是Java7中增加的,是为实现动态类型的语言做的一种改进,但是在java7中并没有直接提供生成该指令的方法,需要借助ASM底层字节码工具来产生指令,直到java8的lambda表达式的出现,该指令才有了直接的生成方式。

「小知识点:静态类型语言与动态类型语言」

它们的区别就在于对类型的检查是在编译期还是在运行期,满足前者就是静态类型语言,反之是动态类型语言。即静态类型语言是判断变量自身的类型信息,动态类型语言是判断变量值的类型信息,变量没有类型信息,变量值才有类型信息,这是动态语言的一个重要特征。

「例」java类中定义的基本数据类型,在声明时就已经确定了他的具体类型了;而js中用var来定义类型,值是什么类型就会在调用时使用什么类型。

虚方法与非虚方法

字节码指令集为invokestatic、invokespecial或者是用final修饰的invokevirtual的方法的话,都可以在解析阶段中确定唯一的调用版本,符合这个条件的就是我们上边提到的五类方法。它们在类加载的时候就会把符号引用解析为该方法的直接引用,这些方法可以称为「非虚方法」。与之相反,不是非虚方法的方法是「虚方法」。图片

分派

如果我们在编译期间没有将方法的符号引用转化为直接引用,而是在运行期间根据方法的实际类型绑定相关的方法,我们把这种方法的调用称为分派。其中分派又分为静态分派和动态分派。

静态分派

不知道你对重载了解多少?为了解释静态分派,我们先来个重载的小测试

public class StaticDispatch {          static abstract class 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();         StaticDispatch sr = new StaticDispatch();         sr.sayHello(man);         sr.sayHello(woman);     } }
hello,guy! hello,guy!

你答对了嘛?首先我们来了解两个概念:静态类型和实际类型。拿Human man = new  Man();来说Human称为变量的静态类型,而Man我们称为变量的实际类型,区别如下:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2. 静态类型的变化仅仅在使用时才发生,变量本身的静态类型是不会被改变,并且最终静态类型在编译期是可知的。

  3. 实际类型的变化是在运行期才知道,编译器在编译程序时并不知道一个对象的具体类型是什么。

此处之所以执行的是Human类型的方法,是因为编译器在重载时,会通过参数的「静态类型」来作为判定执行方法的依据,而不是使用「实际类型」。

所有依赖静态类型来定位方法执行版本的分派动作称为静态分派。静态分派的典型应用就是方法重载。静态分派发生在编译阶段,因此确定静态分派的动作实际上不是由虚拟机来执行的,而是由编译器来完成。

动态分派

了解了重载之后再来了解下重写?案例走起:

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!

这次相信大家的结果都对了吧?我们先来补充一个知识点:

父类引用指向子类时,如果执行的父类方法在子类中未被重写,则调用自身的方法;如果被子类重写了,则调用子类的方法。如果要使用子类特有的属性和方法,需要向下转型。

根据这个结论我们反向推理一下:man和women是静态类型相同的变量,它们在调用相同的方法sayHello()时返回了不同的结果,并且在变量man的两次调用中执行了不同的方法。导致这个现象的原因很明显,是这两个变量的「实际类型」不同,Java虚拟机是如何根据实际类型来分派方法执行版本的呢?我们看下字节码文件:

web开发中的方法调用是怎样的

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

我们关注的是以上两行代码,他们对应的分别是17和21行的字节码指令。单从字节码指令角度来看,它俩的指令invokevirtual和常量$Human.sayHello:()V是完全一样的,但是执行的结果确是不同的,所以我们得研究下invokevirtual指令了,操作流程如下:图片

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

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

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

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

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

由于invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次调用中的invokevirtual指令并不是把常量池中方法的符号引用解析到直接引用上就结束了,还会根据接收者的实际类型来选择方法版本(案例中的实际类型为Man和Woman),这个过程就是Java语言中方法重写的「本质」。

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

单分派与多分派

方法的接收者与方法的参数统称为方法的宗量,这个定义最早应该来源于《Java与模式》一书。根据分派基于多少种宗量,可以将分派划分为单分派和多分派两种。单分派是根据一个宗量对目标方法进行选择,多分派则是根据多于一个宗量对目标方法进行选择。

「举例说明」

public class Dispatch{     static class QQ{}     static class_360{}          public static class Father{         public void hardChoice(QQ arg){             System.out.println("father choose qq");         }         public void hardChoice(_360 arg){             System.out.println("father choose 360");         }     }     public static class Son extends Father{         public void hardChoice(QQ arg){             System.out.println("son choose qq");         }         public void hardChoice(_360 arg){             System.out.println("son choose 360");         }     }     public static void main(String[]args){         Father father=new Father();         Father son=new Son();         father.hardChoice(new_360());         son.hardChoice(new QQ());     } }

请考虑一下输出结果,继续沉默两分钟。答案是:

father choose 360 son choose qq

我们来看看编译阶段编译器的选择过程,也就是静态分派的过程。这时选择目标方法的依据有两点:一是静态类型是Father还是Son,二是方法参数是QQ还是360。这次选择结果的最终产物是产生了两条invokevirtual指令,两条指令的参数分别为常量池中指向Father.hardChoice(360)及Father.hardChoice(QQ)方法的符号引用。因为是根据两个宗量进行选择,所以Java语言的静态分派属于多分派类型。

再看看运行阶段虚拟机的选择,也就是动态分派的过程。在执行“son.hardChoice(new  QQ())”这句代码时,更准确地说,是在执行这句代码所对应的invokevirtual指令时,由于编译期已经决定目标方法的签名必须为hardChoice(QQ),虚拟机此时不会关心传递过来的参数“QQ”到底是“腾讯QQ”还是“奇瑞QQ”,因为这时参数的静态类型、实际类型都对方法的选择不会构成任何影响,唯一可以影响虚拟机选择的因素只有此方法的接受者的实际类型是Father还是Son。因为只有一个宗量作为选择依据,所以Java语言的动态分派属于单分派类型。

虚方法表

面向对象编程中,会很频繁的使用到动态分派,如果在每次动态分派的过程中都要重新在类的方法元数据中搜索合适的目标的话就很可能影响到执行效率。因此,为了提高性能,jvm采用在类的方法区建立一个虚方法表(Vritual  Method Table,也称为vtable,与此对应的,在invokeinterface执行时也会用到接口方法表——Inteface Method  Table,简称itable)来实现,使用虚方法表索引来代替元数据查找以提高性能。

web开发中的方法调用是怎样的

每一个类中都有一个虚方法表,表中存放着各种方法的实际入口:

  • 如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口。

  • 如果子类中重写了这个方法,子类方法表中的地址将会替换为指向子类实现版本的入口地址。

Son重写了来自Father的全部方法,因此Son的方法表没有指向Father类型数据的箭头。但是Son和Father都没有重写来自Object的方法,所以它们的方法表中所有从Object继承来的方法都指向了Object的数据类型。

为了程序实现上的方便,具有相同签名的方法,在父类、子类的虚方法表中都应当具有一样的索引序号,这样当类型变换时,仅需要变更查找的方法表,就可以从不同的虚方法表中按索引转换出所需的入口地址。方法表一般在类加载的连接阶段进行初始化,准备了类的变量初始值后,虚拟机会把该类的方法表也初始化完毕。

绑定机制

解析调用一定是个静态的过程,在编译期间就完全确定,在类装载的解析阶段就会把涉及的符号引用全部转变为可确定的直接引用,不会延迟到运行期再去完成。分派(Dispatch)调用则可能是静态的也可能是动态的。因此我们把  「解析」 和 「静态分派」 这俩在编译期间就确定了被调用的方法,且在运行期间不变的调用称之为静态链接,而在运行期才确定下来调用方法的称之为动态链接。

我们把在静态链接过程中的转换成为早期绑定,将动态链接过程中的转换称之为晚期绑定。

关于WEB开发中的方法调用是怎样的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。

--结束END--

本文标题: web开发中的方法调用是怎样的

本文链接: https://lsjlt.com/news/81543.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • web开发中的方法调用是怎样的
    这篇文章将为大家详细讲解有关web开发中的方法调用是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。方法调用是不是很熟悉那...
    99+
    2024-04-02
  • web开发中的线程是怎样的
    web开发中的线程是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。中央处理器的调度单元,简单点说就是程序中的末端执行者,...
    99+
    2024-04-02
  • web开发中的分布式事务是怎样的
    web开发中的分布式事务是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。事务(Transaction):一般是指要做的或...
    99+
    2024-04-02
  • web开发安全框架中的Apache Shiro的应用是怎样的
    今天给大家介绍一下web开发安全框架中的Apache Shiro的应用是怎样的。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。web开发安全框架中的Apache...
    99+
    2023-06-02
  • web前端中js reduce方法的使用是怎样的
    web前端中js reduce方法的使用是怎样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。web前端教程分享js reduce方法使用教程,reduce() 方法接收一个函...
    99+
    2023-06-04
  • 理想的Java Web开发框架是怎样的
    今天就跟大家聊聊有关理想的Java Web开发框架是怎样的,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。理想的Java Web开发框架,应该有一个好的IDE开发工具,架构设计清晰简单...
    99+
    2023-06-17
  • SpringBoot Web开发的方法
    本篇内容主要讲解“SpringBoot Web开发的方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“SpringBoot Web开发的方法”吧! 一、简介 ...
    99+
    2024-04-02
  • web开发中iframe的跨域使用方法
    这篇文章给大家分享的是有关web开发中iframe的跨域使用方法的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。   同一个origin下:   父页面可以通过iframe.c...
    99+
    2024-04-02
  • web开发中常用的js方法汇总
    这篇文章主要讲解了“web开发中常用的js方法汇总”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“web开发中常用的js方法汇总”吧!1.网页图片集左右滑动查...
    99+
    2024-04-02
  • 外web开发中函数怎么调用
    这篇文章主要讲解了“外web开发中函数怎么调用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“外web开发中函数怎么调用”吧!一、函数具有某种特定功能的一段代码,函数简化了代码,提高了代码的复...
    99+
    2023-06-04
  • web工厂方法的结构是怎样的
    本篇内容介绍了“web工厂方法的结构是怎样的”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!结 构光从类图的角度来看,工厂方法和抽象工厂的类图...
    99+
    2023-06-19
  • Web开发的网页浏览器现状是怎样的
    Web开发的网页浏览器现状是怎样的,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。现在的互联网网络浏览器市场给我们呈现的是一个变...
    99+
    2024-04-02
  • 基于Django Web开发的系统清单是怎样的
    这篇文章给大家介绍基于Django  Web开发的系统清单是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一 简介   最近在和 同事 一起开发一套数据库管理平台...
    99+
    2023-06-04
  • web开发中对于长尾关键词的理解是怎样的
    本篇文章给大家分享的是有关web开发中对于长尾关键词的理解是怎样的,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。网站上非目标关键词但也可以带来...
    99+
    2024-04-02
  • Android开发中怎样调用系统Email发送邮件(多种调用方式)
    我们都知道,在Android中调用其他程序进行相关处理,几乎都是使用的Intent,所以,Email也不例外。 在Android中,调用Email有三种类型的Intent: I...
    99+
    2022-06-06
    调用 android开发 email 邮件 Android
  • 自定义用于Web开发的开源PHP框架Codeigniter是怎么样的
    自定义用于Web开发的开源PHP框架Codeigniter是怎么样的,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。Codeigniter 是一个 PHP 框架,可以使公司进行开...
    99+
    2023-06-16
  • Web开发之canvas2image的用法是什么
    canvas2image是一个javascript库,用于将HTML5 canvas元素转换为图像。它的用法如下:1. 引入canv...
    99+
    2023-10-20
    Web开发
  • web开发中数据结构线性结构链表是怎样的
    这篇文章给大家介绍web开发中数据结构线性结构链表是怎样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。一、前言我们今天要讲解的 链表 不一样,链表是我们数据结构学习的一个重点,也有可...
    99+
    2024-04-02
  • web开发中低代码开发会带来怎样的安全风险
    今天就跟大家聊聊有关web开发中低代码开发会带来怎样的安全风险,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Gartner 还预测,在疫情期间远程开...
    99+
    2024-04-02
  • web开发中做外链的常用方法有哪些
    web开发中做外链的常用方法有哪些,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。“内容为王,外链为皇”的说法,说明了外链的重要性。做外链的方法...
    99+
    2024-04-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作