java的动态绑定
所谓的动态绑定就是指程执行期间(而不是在编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。java继承体系中的覆盖就是动态绑定的,看一下如下的代码:
- classFather{
- publicvoidmethod(){
- System.out.println("ThisisFather'smethod");
- }
- }
- classSon1extendsFather{
- publicvoidmethod(){
- System.out.println("ThisisSon1'smethod");
- }
- }
- classSon2extendsFather{
- publicvoidmethod(){
- System.out.println("ThisisSon2'smethod");
- }
- }
- publicclassTest{
- publicstaticvoidmain(String[]args){
- Fathers1=newSon1();
- s1.method();
- Fathers2=newSon2();
- s2.method();
- }
- }
运行结果如下:
This is Son1's method
This is Son2's method
通过运行结果可以看到,尽管我们引用的类型是Father类型的,但是运行时却是调用的它实际类型(也就是Son1和Son2)的方法,这就是动态绑定。在java语言中,继承中的覆盖就是是动态绑定的,当我们用父类引用实例化子类时,会根据引用的实际类型调用相应的方法。
java的静态绑定
相对于动态绑定,静态绑定就是指在编译期就已经确定执行哪一个方法。在java中,方法的重载(方法名相同而参数不同)就是静态绑定的,重载时,执行哪一个方法在编译期就已经确定下来了。看一下代码:
- classFather{}
- classSon1extendsFather{}
- classSon2extendsFather{}
- classExecute{
- publicvoidmethod(Fatherfather){
- System.out.println("ThisisFather'smethod");
- }
- publicvoidmethod(Son1son){
- System.out.println("ThisisSon1'smethod");
- }
- publicvoidmethod(Son2son){
- System.out.println("ThisisSon2'smethod");
- }
- }
- publicclassTest{
- publicstaticvoidmain(String[]args){
- Fatherfather=newFather();
- Fathers1=newSon1();
- Fathers2=newSon2();
- Executeexe=newExecute();
- exe.method(father);
- exe.method(s1);
- exe.method(s2);
- }
- }
运行结果如下:
This is Father's method
This is Father's method
This is Father's method
在这里,程序在编译的时候就已经确定使用method(Father father)方法了,不管我们在运行的时候传入的实际类型是什么,它永远都只会执行method(Father father)这个方法。也就是说,java的重载是静态绑定的。
instanceof操作符与转型
有时候,我们希望在使用重载的时候,程序能够根据传入参数的实际类型动态地调用相应的方法,也就是说,我们希望java的重载是动态的,而不是静态的。但是由于java的重载不是动态绑定,我们只能通过程序来人为的判断,我们一般会使用instanceof操作符来进行类型的判断。我们要对method(Father father)进行修改,在方法体中判断运行期间的实际类型,修改后的method(Father father)方法如下:
- publicvoidmethod(Fatherfather){
- if(fatherinstanceofSon1){
- method((Son1)father);
- }elseif(fatherinstanceofSon2){
- method((Son2)father);
- }elseif(fatherinstanceofFather){
- System.out.println("ThisisFather'smethod");
- }
- }
请注意,我们必须把判断是否是父类的条件(也就是判断是否为Father类的条件)放到最后,否则将一律会被判断为Father类,达不到我们动态判断的目的。修改代码后,程序就可以动态地根据参数的实际类型来调用相应的方法了。运行结果如下:
This is Father's method
This is Son1's method
This is Son2's method
但是这种实现方式有一个明显的缺点,它是伪动态的,仍然需要我们来通过程序来判断类型。假如Father有100个子类的话,还是这样来实现显然是不合适的。必须通过其他更好的方式实现才行,我们可以使用双分派方式来实现动态绑定。
用双分派实现动态绑定
首先,什么是双分派?还记得23种设计模式(9):访问者模式中一开始举的例子吗?
类A中的方法method1和method2的区别就是,method2是双分派。我们可以看一下java双分派的特点:首先要有一个访问类B,类B提供一个showA(A a) 方法,在方法中,调用类A的method1方法,然后类A的method2方法中调用类B的showA方法并将自己作为参数传给showA。双分派的核心就是这个this对象。说到这里,我们已经明白双分派是怎么回事了,但是它有什么效果呢?就是可以实现方法的动态绑定,我们可以对上面的程序进行修改,代码如下:
- classFather{
- publicvoidaccept(Executeexe){
- exe.method(this);
- }
- }
- classSon1extendsFather{
- publicvoidaccept(Executeexe){
- exe.method(this);
- }
- }
- classSon2extendsFather{
- publicvoidaccept(Executeexe){
- exe.method(this);
- }
- }
- classExecute{
- publicvoidmethod(Fatherfather){
- System.out.println("ThisisFather'smethod");
- }
- publicvoidmethod(Son1son){
- System.out.println("ThisisSon1'smethod");
- }
- publicvoidmethod(Son2son){
- System.out.println("ThisisSon2'smethod");
- }
- }
- publicclassTest{
- publicstaticvoidmain(String[]args){
- Fatherfather=newFather();
- Fathers1=newSon1();
- Fathers2=newSon2();
- Executeexe=newExecute();
- father.accept(exe);
- s1.accept(exe);
- s2.accept(exe);
- }
- }
可以看到我们修改的地方,在Father,Son1,Son2中分别加入一个双分派的方法。调用的时候,原本是调用Execute的method方法,现在改为调用Father的accept方法。运行结果如下:
This is Father's method
This is Son1's method
This is Son2's method
运行结果符合我们的预期,实现了动态绑定。双分派实现动态绑定的本质,就是在重载方法委派的前面加上了继承体系中覆盖的环节,由于覆盖是动态的,所以重载就是动态的了,与使用instanceof操作符的效果是一样的(用instanceof操作符可以实现重载方法动态绑定的原因也是因为instanceof操作符是动态的)。但是与使用instanceof操作符实现动态绑定相比,双分派方式的可扩展性要好的多。
分享到:
相关推荐
在Java中,结合动态绑定和静态绑定的概念,访问者模式可以帮助我们更好地处理复杂对象结构的遍历和操作。动态绑定提供了多态性,使得子类可以覆盖父类的行为;而静态绑定则确保了代码的清晰性和效率,使得编译器能...
在支持双分派的语言中,可以更灵活地处理动态绑定,从而在某些情况下不需要使用访问者模式。 主流的面向对象语言如Java、C++和C#只支持单分派。例如,在Java中,多态特性允许在运行时确定对象的实际类型,然后调用...
Java的动态绑定与双分派是面向对象编程中两个重要的概念,它们关乎程序在运行时如何选择正确的方法执行。动态绑定,又称晚期绑定或虚函数调用,是Java等面向对象语言的一种特性,它允许在运行时根据对象的实际类型...
为了解决这一问题,访问者模式可以采用双分派实现,即在元素类中也包含一个访问者类型参数,这样在访问者接口中只需要声明一个通用的 visit 方法,而元素类则根据自身类型决定调用哪个具体方法。 双分派的实现原理...
本节应用命令模式,在Java中模拟双分派。理解本节后,访问者模式(visitor pattern)手到擒来。分派/dispatch是指如何给一个消息绑定其方法体。Java、C#等仅仅支持单分派(singledispatch)而不支持双分派(double ...
- **名称解析与绑定**:学习如何使用JNDI来进行名称解析和绑定。 ##### 30. 企业Java Beans (Lesson32) - **EJB概述**:介绍企业Java Beans的基本概念及其分类。 - **会话Bean与实体Bean**:学习如何使用会话Bean...
- **生产者消费者模式**:典型的应用场景之一。 - **5.4 线程生命的控制**: - **join()方法**:等待线程结束。 - **interrupt()**:中断线程。 #### 第6章:Java API - **6.1 理解API的概念**: - **定义**...
设计模式之 Visitor(访问者) 访问者在进行访问时,完成一系列实质性操作,而且还可以扩展. 设计模式引言 设计面向对象软件比较困难,而设计可复用的面向对象软件就更加困难。你必须找到相关的对象,以适当的粒度将...
SpringMVC是Java Web开发中的一个关键框架,它在MVC(Model-View-Controller)设计模式的基础上,为开发者提供了构建高效、灵活的Web应用程序的能力。这个名为"SpringMVC精品资源--这是一个Java开发宝典,所有的Java...
当客户机第一次调用一个Stateful Session Bean 时,容器必须立即在服务器中创建一个新的Bean实例,并关联到客户机上,以后此客户机调用Stateful Session Bean 的方法时容器会把调用分派到与此客户机相关联的Bean实例...
Java是一种广泛使用的面向对象的编程语言,其特点包括抽象、继承、封装和多态性。在面试中,这些基础知识通常是考察的重点。 1. 抽象:抽象是将复杂的问题简化,忽略不重要的细节,专注于核心功能。在Java中,抽象...
在Java编程语言中,面试题通常涵盖了许多...以上只是Java面试中常见的一些知识点,实际面试中还可能涉及到线程、集合框架、IO流、网络编程、设计模式等更多内容。熟悉这些基础概念和技术对于提升Java开发能力至关重要。
【Java面试题及答案】 1. **面向对象的特征** - 抽象:抽象是将复杂的现实问题简化,关注核心特性,忽略不重要的细节。它包括过程抽象和数据抽象,使得代码更具可读性和可维护性。 - 继承:继承允许新类从现有类...
SpringMVC 和 MyBatis 是两个非常流行的 Java 开发框架,它们在企业级 Web 应用开发中扮演着重要角色。SpringMVC 是 Spring 框架的一部分,主要用于构建 MVC(Model-View-Controller)模式的 Web 应用,而 MyBatis ...
3. **服务层**:Spring Service组件,可以创建业务逻辑层的服务,通过依赖注入与数据访问层交互。 ### Spring MVC Spring MVC是Spring框架的一个模块,专门用于构建Web应用程序。它提供了一种模型-视图-控制器...
Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个“springMVC教程.rar”压缩包包含了一个完整的Spring MVC学习资源,从基础概念到实际项目...
Java面试题目涵盖了许多核心的Java编程概念和技术,其中包括面向对象的四大特征、基本类型与封装类的区别、字符串处理以及异常处理等。以下是这些知识点的详细解释: 1. **面向对象的特征** - **抽象**:抽象是...
这个项目对于初学者来说,涵盖了Spring MVC的完整流程,包括了前端控制器、模型绑定、视图解析、数据库操作等多个方面,是学习和实践Web开发的好材料。通过阅读源代码,可以深入了解每个组件的作用以及它们之间的...