精华内容
下载资源
问答
  • Java 多态的实现机制
    2021-02-27 13:02:54

    下面从虚拟机运行时的角度来简要介绍多态的实现原理,这里以Java虚拟机(Java Virtual Machine, JVM)规范的实现为例。

    在JVM执行Java字节码时,类型信息被存放在方法区中,通常为了优化对象调用方法的速度,方法区的类型信息中增加一个指针,该指针指向一张记录该类方法入口的表(称为方法表),表中的每一项都是指向相应方法的指针。

    方法表的构造如下:

    由于Java的单继承机制,一个类只能继承一个父类,而所有的类又都继承自Object类。方法表中最先存放的是Object类的方法,接下来是该类的父类的方法,最后是该类本身的方法。这里关键的地方在于,如果子类改写了父类的方法,那么子类和父类的那些同名方法共享一个方法表项,都被认作是父类的方法。

    注意这里只有非私有的实例方法才会出现,并且静态方法也不会出现在这里,原因很容易理解:静态方法跟对象无关,可以将方法地址直接引用,而不像实例方法需要间接引用。

    更深入地讲,静态方法是由虚拟机指令invokestatic调用的,私有方法和构造函数则是由invokespecial指令调用,只有被invokevirtual和invokeinterface指令调用的方法才会在方法表中出现。

    由于以上方法的排列特性(Object——父类——子类),使得方法表的偏移量总是固定的。例如,对于任何类来说,其方法表中equals方法的偏移量总是一个定值,所有继承某父类的子类的方法表中,其父类所定义的方法的偏移量也总是一个定值。

    前面说过,方法表中的表项都是指向该类对应方法的指针,这里就开始了多态的实现:

    假设Class A是Class B的子类,并且A改写了B的方法method(),那么在B的方法表中,method方法的指针指向的就是B的method方法入口。

    而对于A来说,它的方法表中的method方法则会指向其自身的method方法而非其父类的(这在类加载器载入该类时已经保证,同时JVM会保证总是能从对象引用指向正确的类型信息)。

    结合方法指针偏移量是固定的以及指针总是指向实际类的方法域,我们不难发现多态的机制就在这里:

    在调用方法时,实际上必须首先完成实例方法的符号引用解析,结果是该符号引用被解析为方法表的偏移量。虚拟机通过对象引用得到方法区中类型信息的入口,查询类的方法表,当将子类对象声明为父类类型时,形式上调用的是父类方法,此时虚拟机会从实际类的方法表(虽然声明的是父类,但是实际上这里的类型信息中存放的是子类的信息)中查找该方法名对应的指针(这里用“查找”实际上是不合适的,前面提到过,方法的偏移量是固定的,所以只需根据偏移量就能获得指针),进而就能指向实际类的方法了。

    我们的故事还没有结束,事实上上面的过程仅仅是利用继承实现多态的内部机制,多态的另外一种实现方式:实现接口相比而言就更加复杂,原因在于,Java的单继承保证了类的线性关系,而接口可以同时实现多个,这样光凭偏移量就很难准确获得方法的指针。所以在JVM中,多态的实例方法调用实际上有两种指令:

    invokevirtual指令用于调用声明为类的方法;

    invokeinterface指令用于调用声明为接口的方法。

    当使用invokeinterface指令调用方法时,就不能采用固定偏移量的办法,只能老老实实挨个找了(当然实际实现并不一定如此,JVM规范并没有规定究竟如何实现这种查找,不同的JVM实现可以有不同的优化算法来提高搜索效率)。我们不难看出,在性能上,调用接口引用的方法通常总是比调用类的引用的方法要慢。这也告诉我们,在类和接口之间优先选择接口作为设计并不总是正确的,当然设计问题不在本文探讨的范围之内,但显然具体问题具体分析仍然不失为更好的选择。

    个人见解:多态机制包括静态多态(编译时多态)和动态多态(运行时多态),静态多态比如说重载,动态多态是在编译时不能确定调用哪个方法,得在运行时确定。动态多态的实现方法包括子类继承父类和类实现接口。当多个子类上转型(不知道这么说对不)时,对象掉用的是相应子类的方法,这种实现是与JVM有关的。

    更多相关内容
  • Java动态多态原理

    2021-02-27 13:02:57
    多态分为几种:多态分为静态多态动态多态什么是静态多态静态多态性指的是程序在编译时,系统就能决定调用哪个函数,一般指的是重载,是在一个类中的行为。重载,就是在类中创建多个方法,它们具有相同的名字,但...

    很多书本说到多态,概念就把人看晕了,本人从实际使用角度,来介绍多态,希望对大家有用。

    什么是多态:

    多态简单来说,就是调用同一个函数名字,会出现不同的运行结果。

    多态分为几种:

    多态分为静态多态、动态多态

    什么是静态多态

    静态多态性指的是程序在编译时,系统就能决定调用哪个函数,一般指的是重载,是在一个类中的行为。

    重载,就是在类中创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性

    例如:

    public class Dog {

    void bark()//bark()方法是重载方法

    {

    System.out.println(\'no barking!\');

    this.bark(\'female\', 3.4);

    }

    void bark(String m,double l)//注意:重载的方法的返回值都是一样的,

    {

    System.out.println(\'a barking dog!\');

    this.bark(5, \'China\');

    }

    void bark(int a,String n)//不能以返回值区分重载方法,而只能以“参数类型”和“类名”来区分

    {

    System.out.println(\'a howling dog\');

    }

    }

    什么是动态多态

    动态多态性是指方法的重写,是在有继承关系的两个或两个以上的类中的行为。一般子类通过实现父类函数,允许基类的指针或引用指向派生类的对象,而在具体访问时实现方法的动态绑定。

    以java为例:

    public class Fruit {……

    public void show_name(int num){

    System.out.println('Fruit: '+mum);

    }

    }

    public class Apple extends Fruit{

    @Override

    public void show_name(int num){

    System.out.println('Apple');

    }

    }

    如何实现动态多态

    方法表与方法调用

    方法表是动态调用的核心,也是 Java 实现动态调用的主要方式。它被存储于方法区中的类型信息,包含有该类型所定义的所有方法及指向这些方法代码的指针,注意这些具体的方法代码可能是被覆写的方法,也可能是继承自基类的方法。

    如有类定义 Person, Girl, Boy,

    清单 1

    class Person {

    public String toString(){

    return 'I'm a person.';

    }

    public void eat(){}

    public void speak(){}

    }

    class Boy extends Person{

    public String toString(){

    return 'I'm a boy';

    }

    public void speak(){}

    public void fight(){}

    }

    class Girl extends Person{

    public String toString(){

    return 'I'm a girl';

    }

    public void speak(){}

    public void sing(){}

    }

    当这三个类被载入到 Java 虚拟机之后,方法区中就包含了各自的类的信息。Girl 和 Boy 在方法区中的方法表可表示如下:

    图 3.Boy 和 Girl 的方法表

    84264329_1

    可以看到,Girl 和 Boy 的方法表包含继承自 Object 的方法,继承自直接父类 Person 的方法及各自新定义的方法。注意方法表条目指向的具体的方法地址,如 Girl 的继承自 Object 的方法中,只有 toString() 指向自己的实现(Girl 的方法代码),其余皆指向 Object 的方法代码;其继承自于 Person 的方法 eat() 和 speak() 分别指向 Person 的方法实现和本身的实现。

    Person 或 Object 的任意一个方法,在它们的方法表和其子类 Girl 和 Boy 的方法表中的位置 (index) 是一样的。这样 JVM 在调用实例方法其实只需要指定调用方法表中的第几个方法即可。

    如调用如下:

    清单 2

    class Party{

    void happyHour(){

    Person girl = new Girl();

    girl.speak();

    }

    }

    当编译 Party 类的时候,生成 girl.speak()的方法调用假设为:

    Invokevirtual #12

    设该调用代码对应着 girl.speak(); #12 是 Party 类的常量池的索引。JVM 执行该调用指令的过程如下所示:

    图 4. 解析调用过程

    84264329_2

    JVM 首先查看 Party 的常量池索引为 12 的条目(应为 CONSTANT_Methodref_info 类型,可视为方法调用的符号引用),进一步查看常量池(CONSTANT_Class_info,CONSTANT_NameAndType_info ,CONSTANT_Utf8_info)可得出要调用的方法是 Person 的 speak 方法(注意引用 girl 是其基类 Person 类型),查看 Person 的方法表,得出 speak 方法在该方法表中的偏移量 15(offset),这就是该方法调用的直接引用。

    当解析出方法调用的直接引用后(方法表偏移量 15),JVM 执行真正的方法调用:根据实例方法调用的参数 this 得到具体的对象(即 girl 所指向的位于堆中的对象),据此得到该对象对应的方法表 (Girl 的方法表 ),进而调用方法表中的某个偏移量所指向的方法(Girl 的 speak() 方法的实现)。

    接口调用

    因为 Java 类是可以同时实现多个接口的,而当用接口引用调用某个方法的时候,情况就有所不同了。Java 允许一个类实现多个接口,从某种意义上来说相当于多继承,这样同样的方法在基类和派生类的方法表的位置就可能不一样了。

    清单 3

    interface IDance{

    void dance();

    }

    class Person {

    public String toString(){

    return 'I'm a person.';

    }

    public void eat(){}

    public void speak(){}

    }

    class Dancer extends Person

    implements IDance {

    public String toString(){

    return 'I'm a dancer.';

    }

    public void dance(){}

    }

    class Snake implements IDance{

    public String toString(){

    return 'A snake.';

    }

    public void dance(){

    //snake dance

    }

    }

    图 5.Dancer 的方法表

    84264329_3

    可以看到,由于接口的介入,继承自于接口 IDance 的方法 dance()在类 Dancer 和 Snake 的方法表中的位置已经不一样了,显然我们无法通过给出方法表的偏移量来正确调用 Dancer 和 Snake 的这个方法。这也是 Java 中调用接口方法有其专有的调用指令(invokeinterface)的原因。

    Java 对于接口方法的调用是采用搜索方法表的方式,对如下的方法调用

    invokeinterface #13

    JVM 首先查看常量池,确定方法调用的符号引用(名称、返回值等等),然后利用 this 指向的实例得到该实例的方法表,进而搜索方法表来找到合适的方法地址。

    因为每次接口调用都要搜索方法表,所以从效率上来说,接口方法的调用总是慢于类方法的调用的。

    展开全文
  • 静态多态动态多态

    2021-03-14 04:04:38
    java中overload是静态多态,即根据参数列表进行最佳匹配,在编译阶段决定要具体执行哪个方法。而与之相反,overriden methods则是在run-time进行动态检查。举例说明:public class UseAnimals {public void doStuff...

    面向对象编程的多态从绑定时间来看,可以分成静态多态和动态多态,也称为编译期多态和运行期多态。

    java中overload是静态多态,即根据参数列表进行最佳匹配,在编译阶段决定要具体执行哪个方法。而与之相反,overriden methods则是在run-time进行动态检查。

    举例说明:

    public class UseAnimals {

    public void doStuff(Animal a) {

    System.out.println("Animal");

    }

    public void doStuff(Horse a) {

    System.out.println("Horse");

    }

    }

    class Animal{

    public void eat()

    {

    }

    }

    class Horse extends Animal{

    public void eat(String food) {}

    }

    public class TestUseAnimals {

    public static void main(String[] args) {

    UseAnimals ua = new UseAnimals();

    Animal animalobj = new Animal();

    Horse horseobj = new Horse();

    Animal animalRefToHorse = new Horse();

    ua.doStuff(animalobj);//Animal

    ua.doStuff(horseobj);//Horse

    ua.doStuff(animalRefToHorse);//Animal

    }

    }

    可以看见UseAnimals里面有两个 doStuff方法,但参数列表不同,属于overload,在编译阶段决定执行哪个方法,毕竟参数是Animal类型还是Horse类型,这些是编译阶段就可以判断的。但引用类型具体执行哪个方法是根据静态编译阶段来决定的。比如ua.doStuff(animalRefToHorse)  ,animalRefToHorse在编译阶段是判断Animal类型,所以执行doStuff(Animal a)的方法。

    public class UseAnimals {

    public static void main(String[] args) {

    Animal a =new Animal();

    a.eat();

    Horse h = new Horse();

    h.eat();

    Horse ah = new Horse();

    ah.eat();

    Horse he =new Horse();

    he.eat("Apples");

    //Animal ah2=new Animal();

    //ah2.eat("Carrots"); 编译不能通过

    //Animal ah2=new Horse();

    //ah2.eat("Carrots"); 编译不能通过

    }

    }

    class Animal{

    public void eat()

    {

    System.out.println("eat");

    }

    }

    class Horse extends Animal{

    public void eat(String food)

    {

    System.out.println("eat"+food);

    }

    }

    而override则是动态多态,执行方法是在run-time决定

    public class UseAnimals {

    public static void main(String[] args) {

    //Animal a=new Animal();编译不能通过

    Animal a=new Dog();

    Animal b =new Cow();

    a.vocalize();//Woof!

    b.vocalize();//Moo!

    }

    }

    interface Animal{

    void vocalize();

    }

    class Dog implements Animal{

    @Override

    public void vocalize() {

    System.out.println("Woof!");

    }

    }

    class Cow implements Animal{

    @Override

    public void vocalize() {

    System.out.println("Moo!");

    }

    }

    展开全文
  • JAVA中多态性是对象多种表现形式的体现。在面向对象中,最常见的多态发生在使用父类的引用来引用子类的对象。下面这篇文章主要给大家介绍一下,需要的朋友可以参考下
  • Java静态多态动态多态

    千次阅读 2019-04-27 08:24:21
    而实际类型的确定需要在程序运行时才能确定下来,这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。 单分派和多分派 方法的接受者(亦即方法的调用者)与方法的参数统称为方法的宗量。单分派是...

    方法解析

    Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址。这个特性给Java带来了更强大的动态扩展能力,使得可以在类运行期间才能确定某些目标方法的直接引用,称为动态连接,也有一部分方法的符号引用在类加载阶段或第一次使用时转化为直接引用,这种转化称为静态解析

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

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

    解析调用一定是个静态过程,在编译期间就完全确定,在类加载的解析阶段就会把涉及的符号引用转化为可确定的直接引用,不会延迟到运行期再去完成。而分派调用可能是静态的也可能是动态的,根据分派依据的宗量数(方法的调用者和方法的参数统称为方法的宗量)又可分为单分派和多分派。两类分派方式两两组合便构成了四种分派情况:

    • 静态单分派
    • 静态多分派
    • 动态单分派
    • 动态多分派

    静态分派

    所有依赖静态类型来定位方法执行版本的分派动作,都称为静态分派,静态分派的最典型应用就是多态性中的方法重载。

    静态分派发生在编译阶段,因此确定静态分配的动作实际上不是由虚拟机来执行的。下面通过一段方法重载的示例程序来更清晰地说明这种分派机制:

    class Human{
    } 
    class Man extends Human{
    }
    class Woman extends Human{
    }
    public class StaticPai{
    	public void say(Human hum){
    		System.out.println("I am human");
    	}
    	public void say(Man hum){
    		System.out.println("I am man");
    	}
    	public void say(Woman hum){
    		System.out.println("I am woman");
    	}
    	public static void main(String[] args){
    		Human man = new Man();
    		Human woman = new Woman();
    		StaticPai sp = new StaticPai();
    		sp.say(man);
    		sp.say(woman);
    	}
    }
    

    上面代码的执行结果如下:

      I am human 
      I am human
    

    以上结果的得出应该不难分析。在分析为什么会选择参数类型为Human的重载方法去执行之前,先看如下代码:

    Human man = new Man();
    

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

    回到上面的代码分析中,在调用say()方法时,方法的调用者(回忆上面关于宗量的定义,方法的调用者属于宗量)都为sp的前提下,使用哪个重载版本,完全取决于传入参数的数量和数据类型(方法的参数也是数据宗量)。代码中刻意定义了两个静态类型相同、实际类型不同的变量,可见编译器(不是虚拟机,因为如果是根据静态类型做出的判断,那么在编译期就确定了)在重载时是通过参数的静态类型而不是实际类型作为判定依据的。并且静态类型是编译期可知的,所以在编译阶段,Javac编译器就根据参数的静态类型决定使用哪个重载版本。这就是静态分派最典型的应用。

    动态分派

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

    动态分派与多态性的另一个重要体现——方法覆写有着很紧密的关系。向上转型后调用子类覆写的方法便是一个很好地说明动态分派的例子。这种情况很常见,因此这里不再用示例程序进行分析。很显然,在判断执行父类中的方法还是子类中覆盖的方法时,如果用静态类型来判断,那么无论怎么进行向上转型,都只会调用父类中的方法,但实际情况是,根据对父类实例化的子类的不同,调用的是不同子类中覆写的方法,很明显,这里是要根据变量的实际类型来分派方法的执行版本的。而实际类型的确定需要在程序运行时才能确定下来,这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。

    单分派和多分派

    方法的接受者(亦即方法的调用者)与方法的参数统称为方法的宗量。单分派是根据一个宗量对目标方法进行选择,多分派是根据多个宗量对目标方法进行选择。

    • 方法重载属于静态多分派
    • 方法重写属于动态单分派

    们可以总结如下:目前的Java语言是一门静态多分派、动态单分派的语言。

    多态机制遵循的原则概括为:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。


    THE END.

    展开全文
  • 多态是方法的多态,不是属性的多态(多态与属性无关)2.多态存在要有3个必要条件:继承、方法重写、父类引用指向子类对象。3.父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。案例代码:...
  • 多态总结: 指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于...Java实现多态有三个必要条件:继承、重写、向上转型。 继承:在多态中必须存在有继承关系的子类和父类。 重写:子类...
  • 什么是多态多态具体体现在哪些方面? 多态:方法或者对象具有多挣形态,是OOP的第三大特征,是建立在封装和继承...Java动态绑定机制是什么? 1、当调用对象的方法时,该方法会和对象的内存地址(运行地址)绑定 2、
  • java多态动态绑定

    2021-11-26 16:06:07
    java动态动态绑定多态多态的定义多态的分类多态实现的条件多态java中如何实现动态绑定 多态 多态的定义 多态:同一个行为具有多个不同表现形式或形态的能力。 在java中:父类型的变量可以引用子类型的对象 多态...
  • java多态理解

    2018-12-17 14:10:05
    java 个人对于多态理解和一些笔记。
  • 动态绑定 ③ 静态方法 编译看左边(父类),运行看左边(父类)。 多态的弊端:多态后不能使用子类特有的属性和方法 接口比较简单就不一一赘述 看看他们的综合案例实现吧! 1、首先定义一个USB接口: 接口中封装了打开...
  • Java基础之多态动态绑定) 1、概念 多态是面向对象的核心,必须要掌握,多态也叫动态绑定。 多态:在执行期间(而非编译期间)判断所有引用对象的实际类型,根据实际类型调用相应的方法。 多态存在的三个必要...
  • 主要介绍了Java多态动态绑定原理解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
  • JAVA多态图文详解ppt

    2018-03-20 16:46:02
    JAVA多态图文详解ppt,详细通过各种举例介绍JAVA多态的ppt
  • java多态练习题

    2018-04-21 17:52:18
    java多态、继承练习题,包含题目与答案,............................................................................................................
  • JAVA多态的理解

    2022-03-24 20:54:23
    JAVA多态的理解以及使用。
  • java-多态参数

    2021-07-21 19:53:47
    java-多态参数 按照图片顺序源码如下 1 package com.Start300.polyparameter_; public class Employee { private String name; private double salary; public Employee(String name, double salary) { this....
  • java多态的概念

    2022-03-15 00:33:53
    * 1 多态是方法的多态,属性没有多态 * 2父类和子类有联系,注意类型转换ClassCastExeception * 3 存在条件:继承关系,方法需要重写,父类引用指向子类对象 * */ /* * static静态方法不能被重写,属于类方法, ...
  • 10-JAVA-多态

    2020-06-06 15:28:38
    JAVA多态思维导图,便捷整理思路,多态的好处、多态的语法格式、多态中的两种类型转换:向上转型、向下转型
  • 这样就可以不用修改源程序,就可以让引用变量绑定到各种不同的类实现上,Java实现多态有三个必要条件:继承、重定、向上转型,在多态中需要将子类的引用赋值给父类对象,只有这样该引用才能够具备调用父类方法和子类...
  • java多态

    千次阅读 2021-02-12 09:20:29
    1.多态的概述:是面向对象的三大特性之...②Java多态的代码体现在一个子类对象(实现类对象)既可以给这个子类(实现类对象)引用变量赋值,又可以给这个子类(实现类对象)的父类(接口)变量赋值。普通类与普通类,抽象...
  • Java多态的实现原理

    千次阅读 2021-08-26 16:59:08
    这里的接口不应理解得太死板,比如在 Java 里面,继承一个类和实现一个接口本质上都是一种继承行为,因此都可以理解为多态的体现。 从静态和动态的角度进行划分,多态可以分为编译时多态和运行时多态。 编译时多态...
  • Java多态(深入版)

    2020-12-22 09:25:18
    面向对象的三大特性:封装、继承、多态。在这三个特性中,如果没有封装和继承,也不会有多态。 那么多态实现的途径和必要条件是什么呢?以及多态中的重写和重载在JVM中的表现是怎么样? (若文章有不正之处,或难以...
  • 【Java面试题】谈谈对Java多态的理解
  • java 多态实例

    2015-08-25 15:24:39
    一个简单的多态实例,包含interface,abstract class 以及两种排序方式
  • Java-多态

    2021-04-16 01:11:59
    Java-多态Thorn•2019 年 07 月 26 日1.定义左父右子就是多态多态就是一种对象的多种形态父类对象指向子类对象2.实现方法格式:父类名称 对象名 = new 子类名称();或接口名称 对象名 = new 实现类();例:public class ...
  • java参数多态

    2022-02-02 10:49:16
    /* 多态参数: 方法定义的形参类型为父类类型,实参类型允许为子类类型 */ public class PloyParameter { public static void main(String[] args) { Employee worker = new Worker("worker01",3000); Employee ...
  • Java——多态

    2020-12-21 19:51:44
    Java——多态 多态的特性 1.可替换性(substitutability)。多态对已存在代码具有可替换性。 例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环, 也同样工作。 2.可扩充性(extensibility)。多态对代码...
  • JAVA学习笔记,java知识点总结
  • java多态扩展性

    2019-04-23 21:42:40
    java教学视频,讲解了多态的扩展性、转型、成员特点、主板实例、object类等

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 247,591
精华内容 99,036
关键字:

java动态多态

java 订阅