精华内容
下载资源
问答
  • 动态分派

    2018-01-10 17:34:26
    与静态分派不同滴是,动态分派Java虚拟机是根据实际类型来分派方法执行版本。典型例子就是方法的重写。 看代码: package diptch; public class DynamicDispatch { /** * * @author Administrator * */ ...

    与静态分派不同滴是,动态分派Java虚拟机是根据实际类型来分派方法执行版本。典型例子就是方法的重写。

    看代码:

    package diptch;
    
    public class DynamicDispatch {
    
    
        /**
         * 
         * @author Administrator
         *
         */
    
         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!

    拔不出所料赛,结果跟我们想滴一毛毛一样。来看看字节码指令。

      public static void main(java.lang.String[]);
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=3, args_size=1
             0: new           #16                 // class diptch/DynamicDispatch$Ma
    n
             3: dup
             4: invokespecial #18                 // Method diptch/DynamicDispatch$M
    an."<init>":()V
             7: astore_1
             8: new           #19                 // class diptch/DynamicDispatch$Wo
    man
            11: dup
            12: invokespecial #21                 // Method diptch/DynamicDispatch$W
    oman."<init>":()V
            15: astore_2
            16: aload_1
            17: invokevirtual #22                 // Method diptch/DynamicDispatch$H
    uman.sayHello:()V
            20: aload_2
            21: invokevirtual #22                 // Method diptch/DynamicDispatch$H
    uman.sayHello:()V
            24: new           #19                 // class diptch/DynamicDispatch$Wo
    man
            27: dup
            28: invokespecial #21                 // Method diptch/DynamicDispatch$W
    oman."<init>":()V
            31: astore_1
            32: aload_1
            33: invokevirtual #22                 // Method diptch/DynamicDispatch$H
    uman.sayHello:()V
            36: return

    我们分别看21,28,33行的指令都用的是invokevirtual指令,先说这个指令的特点:

    它的执行分为三步:
    1.找到操作数栈顶的第一个元素所指向的对象的实际类型。举个栗子:在我们的20行我们取出了在局部变量表中存储的实际类型儿,就是new出来的women实例。
    2.如果当前实际类型的类中中找到与常量中的描述符和简单名称相符合的方法,然后进行访问权限验证,如果验证通过则返回这个方法的直接引用,查找过程结束;如果验证不通过,则抛出java.lang.IllegalAccessError异常。白话就是在当前类找正在执行的荡秋千方法,找到就直接引用。
    3.、否则未找到,就按照继承关系从下往上依次对各个父类进行第2步的搜索和验证过程。
    看完这个字节码指令的执行过程,我们就知道了是如何寻找实际类型的了,
    invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不同的直接引用上,这个过程就是Java语言方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派。
    所以虽然我们看到
    21: invokevirtual #22 // Method diptch/DynamicDispatch$H
    uman.sayHello:()V

    这样的字节码注释的是Human.sayHello:()V 表面上市静态类型,但是由于是invokevirtual 指令来操作的,所以俺们实际执行的是当前实际类型的方法。

    展开全文
  • 主要介绍了Java的动态分派和静态分派的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
  • 静态分派和动态分派

    2019-08-05 14:24:00
    01 静态分派 所有依赖静态类型来定位方法执行版本的分派成为静态分派,发生在编译时期。 应用为方法重载。静态分派发生在编译阶段,因此确定...向上转型后调用子类覆写的方法便是一个很好地说明动态分派的例子。 ...

    01 静态分派

    所有依赖静态类型来定位方法执行版本的分派成为静态分派,发生在编译时期。

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

    02 动态分派

    典型应用为重写,是一种动态的概念。向上转型后调用子类覆写的方法便是一个很好地说明动态分派的例子。

    就是说,我们在判断执行的方法是父类中的方法还是子类中覆盖的方法时:

    如果用静态类型来判断,那么无论怎么进行向上转型,都只会调用父类中的方法。

    但实际情况是,根据对父类实例化的子类的不同,调用的是不同子类中覆写的方法,很明显,这里是要根据变量的实际类型来分派方法的执行版本的。

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

    动态分派的实现是在方法区中建立方法表

    如果子类没有重写父类的方法,则子类虚方法表中该方法的入口地址与父类指向相同,否则子类方法表中的地址会替换为指向子类重写的方法的入口地址。

    转载于:https://my.oschina.net/u/3938912/blog/3083605

    展开全文
  • 动态分派与静态分派

    2020-02-29 23:25:43
    动态分派: 典型应用为重写,是一种动态的概念。向上转型后调用子类覆写的方法便是一个很好的说明动态分配的例子。很显然在判断执行父类中的方法还是子类中覆盖的方法时,如果用静态类型来判断,那么无论怎么进行...

    包括静态分派和动态分配。

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

    动态分派: 典型应用为重写,是一种动态的概念。向上转型后调用子类覆写的方法便是一个很好的说明动态分配的例子。很显然在判断执行父类中的方法还是子类中覆盖的方法时,如果用静态类型来判断,那么无论怎么进行向上转型,都只会调用父类中的方法。但实际情况是,根据对父类实例化的子类的不同,调用的是不同子类中覆写的方法,很明显,这里要根据变量的实际类型来分配方法的执行版本。而实际类型的确定需要在程序运行时才能确定下来。这种在运行期根据实际类型确定方法执行版本的分配过程称为动态分派。动态分派的实现是在方法区中建立方法表,如果子类中没有重写父类的方法,则子类需方法表中该方法入口地址与父类指向相同,否则方法表中的地址会替换为指向子类重写的方法的入口地址。

    展开全文
  • Java静态分派与动态分派

    万次阅读 多人点赞 2016-08-23 20:20:56
    Java方法调用原理,静态分派与动态分派,重载与重写的本质。

    方法调用并不等于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。

    在程序运行时,进行方法调用是最普遍、最频繁的操作,但是Class文件的编译过程不包括传统编译中的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(相对于之前说的直接引用)。这个特性给Java带来了更强大的动态扩展能力,但也使得Java方法调用过程变得相对复杂起来,需要在类加载期间,甚至到运行期间才能确定目标方法的直接引用。

    解析

    所有方法调用中的目标方法在Class文件里面都是一个常量池中的引用,在类加载的解析阶段,会将其中的一部分符号引用转化为直接引用。这种解析能成立的前提是:方法在程序真正执行之前就有一个可确定的调用版本,并且这个方法的调用版本在运行期是不可改变的。换句话说,调用目标在程序代码写好、编译器进行编译时就必须确定下来,这类方法的调用称为解析

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

    静态方法私有方法实例构造器父类方法。这些方法称为非虚方法,它们在类加载的时候就会把符号引用解析为该方法的直接引用。与之相反,其他方法称为虚方法(除去final方法)。

    分派

    静态分派

    public class StaticDispatch {
    	static abstract class Human{
    	}
    	static class Man extends Human{
    	}
    	static class Woman extends Human{
    	}
    	public static void sayHello(Human guy){
    		System.out.println("hello,guy!");
    	}
    	public static void sayHello(Man guy){
    		System.out.println("hello,gentlemen!");
    	}
    	public static void sayHello(Woman guy){
    		System.out.println("hello,lady!");
    	}
    	
    	public static void main(String[] args) {
    		Human man=new Man();
    		Human woman=new Woman();
    		sayHello(man);
    		sayHello(woman);
    	}
    }
    
    输出:

    hello,guy!
    hello,guy!


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

    		Human man=new Man();
    		sayHello(man);
    		sayHello((Man)man);//类型转换,静态类型变化,我们知道转型后的静态类型一定是Man
    		man=new Woman(); //实际类型变化,实际类型却是不确定的
    		sayHello(man);
    		sayHello((Woman)man);//类型转换,静态类型变化 
    输出:

    hello,guy!
    hello,gentlemen!
    hello,guy!
    hello,lady!

    编译器在重载时是通过参数的静态类型而不是实际类型作为判定的依据。并且静态类型在编译期可知,因此,编译阶段,Javac编译器会根据参数的静态类型决定使用哪个重载版本。

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

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

    但是,字面量没有显示的静态类型,它的静态类型只能通过语言上的规则去理解和推断。

    public class LiteralTest { 
    	/**/
    	public static void sayHello(char arg){
    		System.out.println("hello char");
    	}
    	public static void sayHello(int arg){
    		System.out.println("hello int");
    	}
    	
    	public static void sayHello(long arg){
    		System.out.println("hello long");
    	}
    	
    	public static void sayHello(Character arg){
    		System.out.println("hello Character");
    	}
    	public static void main(String[] args) {
    		sayHello('a');
    	} 
    }
    
    输出:

    hello char
    将重载方法从上向下依次注释,将会得到不同的输出。

    如果编译器无法确定要自定转型为哪种类型,会提示类型模糊,拒绝编译。

    import java.util.Random;
    
    public class LiteralTest { 
    	/**/
    	public static void sayHello(String arg){//新增重载方法
    		System.out.println("hello String");
    	}
    	public static void sayHello(char arg){
    		System.out.println("hello char");
    	} 
    	public static void sayHello(int arg){
    		System.out.println("hello int");
    	}
    	
    	public static void sayHello(long arg){
    		System.out.println("hello long");
    	}
    	
    	public static void sayHello(Character arg){
    		System.out.println("hello Character");
    	}
    	public static void main(String[] args) {
    		Random r=new Random();
    		String s="abc";
    		int i=0;
    		sayHello(r.nextInt()%2!=0?s:i);//编译错误 
    		sayHello(r.nextInt()%2!=0?'a':false);//编译错误
    	} 
    }

    动态分派

    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!


    显然,这里不可能再根据静态类型来决定,因为静态类型同样是Human的两个变量man和woman在调用sayHello()方法时执行了不同的行为,并且变量man在两次调用中执行了不同的方法。导致这个现象的原因很明显,是这两个变量的实际类型不同,Java虚拟机是如何根据实际类型来分派方法执行版本的呢?
    我们从invokevirtual指令的多态查找过程开始说起,invokevirtual指令的运行时解析过程大致分为以下几个步骤:

    1、找到操作数栈顶的第一个元素所指向的对象的实际类型,记作C
    2、如果在类型C中找到与常量中的描述符和简单名称相符合的方法,然后进行访问权限验证,如果验证通过则返回这个方法的直接引用,查找过程结束;如果验证不通过,则抛出java.lang.IllegalAccessError异常。
    3、否则未找到,就按照继承关系从下往上依次对类型C的各个父类进行第2步的搜索和验证过程。
    4、如果始终没有找到合适的方法,则跑出java.lang.AbstractMethodError异常。

    由于invokevirtual指令执行的第一步就是在运行期确定接收者的实际类型,所以两次调用中的invokevirtual指令把常量池中的类方法符号引用解析到了不同直接引用上,这个过程就是Java语言方法重写的本质。我们把这种在运行期根据实际类型确定方法执行版本的分派过程称为动态分派

    虚拟机动态分派的实现

    前面介绍的分派过程,作为对虚拟机概念模型的解析基本上已经足够了,它已经解决了虚拟机在分派中"会做什么"这个问题。

    但是,虚拟机”具体是如何做到的“,可能各种虚拟机实现都会有些差别。

    由于动态分派是非常频繁的动作,而且动态分派的方法版本选择过程需要运行时在类的方法元数据中搜索合适的目标方法,因此虚拟机的实际实现中基于性能的考虑,大部分实现都不会真正的进行如此频繁的搜索。面对这种情况,最常用的”稳定优化“手段就是为类在方法区中建立一个虚方法表(Virtual Method Table,也称为vtable),使用虚方法表索引代替元数据查找以提高性能。


    虚方法表中存放着各个方法的实际入口地址。如果某个方法在子类中没有被重写,那子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都是指向父类的实际入口。如果子类中重写了这个方法,子类方法表中的地址将会替换为指向子类实际版本的入口地址。

    为了程序实现上的方便,具有相同签名的方法,在父类、子类的虚方法表中具有一样的索引序号,这样当类型变换时,仅仅需要变更查找的方法表,就可以从不同的虚方法表中按索引转换出所需要的入口地址。

    方法表一般在类加载阶段的连接阶段进行初始化,准备了类的变量初始值后,虚拟机会把该类的方法表也初始化完毕。

    内容源自:

    《深入理解Java虚拟机》


    展开全文
  • 重载发生在同一个类中,相同方法名参数个数顺序类型不一样,静态分派虚拟机调用静态类型而不是实际类型,静态类型在编译期可知,编译时调用。 ...动态分派虚拟机调用实际类型,在运行时调用。 ...
  • 静态分派和动态分派 运行时多态或动态方法分派 (Runtime Polymorphism or Dynamic method dispatch) Dynamic method dispatch is a mechanism by which a call to an overridden method is resolved at runtime. ...
  • Java 静态分派与动态分派
  • ​向上转型后调用子类覆写的方法便是一个很好地说明动态分派的例子。在判断执行父类中的方法还是子类中覆盖的方法时,如果用静态类型来判断,那么无论怎么进行向上转型,都只会调用父类中的方法,但实际情况是,根据...
  • 变量被声明时的类型叫做变量的明显类型,也称为静态类型变量所引用的对象的真实类型叫做实际类型静态分派:发生在编译时期,分配根据静态类型发生,方法的重载就是静态分派动态分派:发生在运行时期,动态的置换掉...
  • 静态分派与动态分派

    2016-07-28 22:35:58
    本文要解决的问题: 深入理解Java虚拟机,本篇文章分析了静态分配和动态分配。...其中重载属于静态分派,而重写则是动态分派的过程。除了使用分派的方式对方法进行调用之外,还可以使用解析调用,解析调用是在
  • 目录静态分派和重载重载方法匹配优先级动态分派与重写字段永远不会参与多态静态分派是多分派,动态分派是单分派 静态分派和重载 public class StaticDispatch { static abstract class Human { } static class ...
  • 静态连接、动态连接、静态分派、动态分派、单分派、多分派、虚方法、非虚方法,这些定义指的是什么?有什么关系
  • 下面小编就为大家带来一篇JVM 方法调用之动态分派(详解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
  • Java中的静态分派和动态分派方法调用方法解析Java方法分类静态分派 方法调用 在程序运行时,进行方法调用是最普遍,最频繁的操作 方法调用不等于方法执行: 方法调用阶段唯一的任务就是确定被调用的方法版本,即调用哪...
  • 动态分派和静态分派机制是Java多态实现的原理。本文将针对这两种机制进行浅析。 静态分派 静态分派机制最典型的代码示例如下 void test() { Father father = new Son(); //静态分派 print(father); } void ...
  • 其中重载属于静态分派,而重写则是动态分派的过程。除了使用分派的方式对方法进行调用之外,还可以使用解析调用,解析调用是在编译期间就已经确定了,在类装载的解析阶段就会把符号引用转化为直接引用,不会延迟到...
  • 动态分派和静态分派机制是Java多态实现的原理。本文将针对这两种机制进行浅析。 静态分派 静态分派机制最典型的代码示例如下 void test() { Father father = new Son(); //静态分派 print(father); } void ...
  • 深入JVM 动态分派

    2019-03-01 20:10:54
    动态分派与多态性的另一个重要体现: 重写(Override) 有着十分密切的关系。 一个动态分派的例子: public class DynamicDispatch { static abstract class Human { protected abstract void say(); } ...
  • 文章目录Java中的多态继承向上转型向下转型多态(重载是静态分派 重写是动态分派)重载静态分派重写动态分派总结参考链接 Java中的多态 要了解多态,我们先来看一下继承。 继承 public class inheritExmple { public ...
  • 静态分派 依赖静态类型 确定方法执行版本的分派[ ...动态分派的实现依赖在方法区建立的方法表,如果子类没有重写父类的方法,则子类虚方法表中该方法的入口地址与父类指向的相同,为子类重写的方法的入口地址。
  • 对象的静态分派和动态分派 重写属于动态分派,重载属于今天分派 静态分派 public class Main { public static class Father{ } public static class Man extends Father{ } public static class ...
  • 多态性实现机制——静态分派与动态分派
  • 在理解静态分派和动态分派之前我们需要先理解静态类型和动态类型。 静态类型,是在编译期间可知的,什么意思呢,就是在使用javac命令编译java文件时确定的指令集。 动态类型,则是编译期间不可知,只有在运行期间...
  • JVM动态分派过程分析

    2021-03-01 16:25:18
    动态分派,简单举例来说就是运行时期根据实际的实现类型去调用对应的实现方法,多态的实现即是如此。 代码实例: package com.company; /* * 方法动态分派演示 * */ public class DynamicDispatch { static ...
  • 其实分派分为两种,即动态分派和静态分派。我们在了解分派的时候,通常把它们与重写和重载结合到一起。 重载(overload)与静态分派 我们先看一个题: public class Main { static abstract class Father { ...
  • Carson带你学JVM:方法分派模型-静态分派、动态分派

    千次阅读 多人点赞 2019-11-25 07:55:14
    本文全面讲解行为分派的类型:静态 & 动态行为分派,希望你们会喜欢。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 37,713
精华内容 15,085
关键字:

动态分派