Java中的多态(polymorphism)和动态绑定(dynamic binding)

本文通过一个Java示例程序展示了多态(polymorphism)和动态绑定(dynamic binding)的概念。示例中,Employee和Manager类都实现了getSalary方法,但实现方式不同。通过Employee数组存放不同类型对象并调用getSalary方法,演示了多态和动态绑定的过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下面代码为例,存在超类Employee和子类Manager,两个类中都有getSalary方法,但实现不同。

一个对象变量可以引用多种实际类型的现象被称为多态(polymorphism),运行时能够自动选择调用哪个方法的现象称为动态绑定(dynamic binding)。

Employee类型的变量e既可以引用Employee类型又可以引用Manager类型,称为多态;调用e.getSalary时,根据e所引用的实际类型,决定调用Employee.getSalary还是调用Manager.getSalary,称为动态绑定。

需要注意的是,Employee类型的变量e尽管引用了Manager类型,但是不能够调用只定义在Manager子类中的方法(如setBonus),因为编译器将e视为Employee类型。如果要调用Manager类中的方法,需另外声明Manager类型的变量(如boss)进行调用。

import java.util.*;

/**
 * This program demonstrates inheritance.
 * @version 1.21 2004-02-21
 * @author Cay Horstmann
 */
public class ManagerTest {
    public static void main(String[] args)
   {
      // construct a Manager object
      Manager boss = new Manager("Carl Cracker", 80000, 1987, 12, 15);
      boss.setBonus(5000);

      Employee[] staff = new Employee[3];

      // fill the staff array with Manager and Employee objects

      staff[0] = boss;
      staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
      staff[2] = new Employee("Tommy Tester", 40000, 1990, 3, 15);

      // print out information about all Employee objects
      for (Employee e : staff)
         System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
   }
}


class Employee
{
   public Employee(String n, double s, int year, int month, int day)
   {
      name = n;
      salary = s;
      GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
      // GregorianCalendar uses 0 for January
      hireDay = calendar.getTime();
   }

   public String getName()
   {
      return name;
   }

   public double getSalary()
   {
      return salary;
   }

   public Date getHireDay()
   {
      return hireDay;
   }

   public void raiseSalary(double byPercent)
   {
      double raise = salary * byPercent / 100;
      salary += raise;
   }

   private String name;
   private double salary;
   private Date hireDay;
}

class Manager extends Employee
{
    /**
    * @param n the employee's name
    * @param s the salary
    * @param year the hire year
    * @param month the hire month
    * @param day the hire day
    */
   public Manager(String n, double s, int year, int month, int day)
   {
      super(n, s, year, month, day);
      bonus = 0;
   }

   public double getSalary()
   {
      double baseSalary = super.getSalary();
      return baseSalary + bonus;
   }

   public void setBonus(double b)
   {
      bonus = b;
   }

   private double bonus;
}

参考:
Cay S. Horstmann, Gary Cornell. JAVA核心技术卷I:基础知识(第八版).机械工业出版社.2011年4月第1版.第5.1节.

<think>我们正在讨论面向对象编程中的多态运行时动态绑定。根据引用内容,多态分为运行时多态编译时多态。运行时多态通过虚函数(或类似机制)实现,而编译时多态通过函数重载实现。动态绑定发生在运行时,根据对象的实际类型决定调用哪个方法;静态绑定则在编译时确定。引用[1][2]解释了多态的两种类型:运行时多态(动态多态编译时多态(静态多态)。动态多态通过覆盖虚函数实现,在运行时根据对象实际类型决定调用哪个方法。静态多态通过重载实现,在编译时根据参数类型、数量等确定调用哪个方法。引用[3][4]给出了具体的代码示例,展示了运行时多态动态绑定)的工作方式:当基类指针或引用指向派生类对象时,调用虚函数会执行派生类中的重写版本。现在,我将详细解释运行时多态动态绑定:1.**多态Polymorphism)**:在面向对象编程中,多态是指同一个操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。多态分为两种:- **编译时多态(静态绑定)**:通过函数重载运算符重载实现,在编译阶段确定具体调用哪个函数。- **运行时多态动态绑定)**:通过继承虚函数(或类似机制)实现,在程序运行时根据对象的实际类型确定调用哪个函数。2. **运行时多态(动态多态)**:其核心在于使用基类的指针或引用指向派生类对象,并通过该指针或引用调用虚函数。在运行时,系统会根据对象的实际类型(而不是指针或引用的类型)来调用相应的函数。这需要满足两个条件:-基类中声明虚函数(在C++中使用`virtual`关键字,在Java中所有非静态方法默认是虚函数)。-派生类重写(override)该虚函数。3.**动态绑定Dynamic Binding)**:也称为晚期绑定(Late Binding),是指在程序运行期间(而非编译期间)根据对象的实际类型来确定调用哪个函数。动态绑定是实现运行时多态的基础。4. **示例分析**(引用[4]中的Java代码,但原理与C++相同):```javaclass Animal1 {publicString name;public intage;publicAnimal1(String name, intage){this.name= name;this.age= age;}public voideat(){System.out.println(this.name +"正在吃东西");}}classDog1extends Animal1 {publicDog1(String name, intage){super(name, age);}public voideat(){//重写父类的eat方法System.out.println(this.name +"吃狗粮");}}class Cat1 extendsAnimal1{public Cat1(Stringname,int age) {super(name,age);}publicvoid eat() {//重写父类的eat方法System.out.println(this.name+ "吃猫粮");}}publicclass demot {publicstatic voidmain(String[] args) {Animal1 animal= newDog1("小黄",2);//父类引用指向子类对象animal.eat();//输出:小黄吃狗粮}}```在这个例子中:-我们创建了一个`Animal1`类型的引用`animal`,但它指向的是`Dog1`类的对象。-当调用`eat()`方法时,由于在Java中非静态方法默认是虚函数(相当于C++中的`virtual`),所以会动态绑定到`Dog1`类的`eat()`方法,输出“小黄吃狗粮”。5. **动态绑定的实现机制**(以C++为例):-当类中有虚函数时,编译器会为该类生成一个虚函数表(vtable),表中存放虚函数的地址。-每个对象在创建时,会包含一个指向虚函数表的指针(vptr)。-当通过基类指针或引用调用虚函数时,程序会通过对象的vptr找到虚函数表,再根据函数在表中的位置找到实际要调用的函数地址。这一过程在运行时完成。6.**动态绑定的优点**:-提高代码的灵活性可扩展性:可以在不修改现有代码的情况下,通过派生类扩展功能。-实现接口重用:基类定义接口,派生类实现具体功能。7. **动态绑定的缺点**:-运行时开销:每次调用虚函数都需要通过虚函数表查找,比直接调用函数稍慢。-对象占用空间增加:每个对象需要存储一个虚函数表指针。8. **与静态绑定的对比**:|特性|静态绑定|动态绑定||--------------|------------------------------|------------------------------||绑定时间|编译时|运行时||实现机制|函数重载、模板|虚函数||性能|高效(无额外开销)|有运行时开销||灵活性|较低|高||适用场景|编译时确定类型的方法调用|运行时确定类型的方法调用|总结:运行时多态动态绑定)是面向对象编程的核心特性之一,它允许程序在运行时根据对象的实际类型来调用相应的方法,提高了代码的灵活性可扩展性。实现运行时多态的关键是使用虚函数(或类似机制)继承。 §§相关问题§§1.在C++中,虚函数表(vtable)的具体结构是怎样的? 2.动态绑定在多重继承中如何工作?3.除了虚函数,还有哪些机制可以实现运行时多态? 4.动态绑定JavaC++中的实现有何异同?</think>### 面向对象编程中的多态与运行时动态绑定详解 #### 1. **多态的核心概念** 多态Polymorphism)是面向对象三大特性之一,指**同一操作作用于不同对象时产生不同行为**。分为两类: - **编译时多态(静态绑定)** 通过函数重载实现,编译器根据**参数类型数量**在编译阶段确定调用哪个方法。 ```java // 函数重载示例 void print(int x) { ... } // 版本1 void print(String s) { ... } // 版本2 ``` 调用 `print(5)` 时编译器直接绑定版本1 [^1][^2]。 - **运行时多态动态绑定)** 通过继承虚函数实现,**运行时**根据对象的**实际类型**决定调用哪个方法。 ```java Animal animal = new Dog(); // 父类引用指向子类对象 animal.eat(); // 实际调用Dog.eat() ``` 输出结果:"小黄吃狗粮"(见引用[4])[^3][^4]。 #### 2. **运行时动态绑定机制** 动态绑定是运行时多态的实现基础,包含三个核心要素: 1. **继承关系** 子类继承父类(如 `Dog extends Animal`) 2. **虚函数(或等效机制)** 父类方法声明为 `virtual`(C++)或默认虚函数(Java) 3. **向上转型** 父类引用指向子类对象(`Animal animal = new Dog()`) **执行过程**: ```mermaid graph TD A[调用animal.eat] --> B{检查对象实际类型} B -->|实际是Dog| C[调用Dog.eat] B -->|实际是Cat| D[调用Cat.eat] ``` - 当调用 `animal.eat()` 时,JVM/C++运行时会: 1. 查找 `animal` 引用的**实际对象类型**(非声明类型) 2. 根据实际类型(如 `Dog`)调用其重写的方法 [^3][^4] #### 3. **关键特性对比 | **特性** | **静态绑定(编译时多态)** | **动态绑定(运行时多态)** | |----------------|----------------------------------|----------------------------------| | **绑定时机** | 编译阶段 | 运行阶段 | | **决定因素** | 参数类型/数量 | 对象实际类型 | | **实现方式** | 函数重载 | 虚函数+继承 | | **性能** | 高效(直接调用) | 有开销(需运行时查表) | | **灵活性** | 低(行为在编译时固定) | 高(行为可运行时扩展) | #### 4. **设计意义与价值 1. **接口统一性** ```java void makeSound(Animal animal) { animal.speak(); // 统一接口,支持所有Animal子类 } ``` 调用 `makeSound(new Dog())` `makeSound(new Cat())` 产生不同行为 [^3]。 2. **可扩展性** 新增子类(如 `Bird`)无需修改现有代码,符合开闭原则。 3. **降低耦合** 调用方只需关注父类接口,无需知道具体子类实现。 #### 5. **动态绑定实现原理(以Java为例) - **虚方法表(vtable)** 每个类维护一个虚方法表,存储可重写方法的实际地址。 - **对象头中的类型指针** 对象内存布局包含指向类信息的指针,运行时通过该指针查找虚方法表。 - **调用过程伪代码**: ```java // 运行时解析调用 Method method = object.getClass().getMethod("eat"); method.invoke(object); // 动态调用实际对象的方法 ``` > **关键总结**:运行时多态通过**延迟绑定**将方法调用与实现分离,使程序能根据上下文自适应调整行为,这是设计模式(如策略模式、工厂模式)的基础[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值