深入理解动态绑定与多态

动态绑定

Java中的动态绑定,是一种在程序运行时确定方法执行版本的过程。它与多态紧密联系。

在Java中,动态绑定通常发生在以下情况:

  1. 方法覆盖:当子类重写父类的一个方法时,调用该方法的行为会根据对象的实际类型(即运行时类型)来决定,而不是根据引用类型。这就是动态绑定的一个例子。
  2. 接口实现:当一个类实现了一个或多个接口时,它必须实现接口中定义的所有方法。当通过接口类型引用一个实现了该接口的类对象时,调用接口方法的行为也是动态绑定的。
  3. 继承:在继承体系中,子类会继承父类的属性和方法。当子类重写父类的方法时,动态绑定决定了调用哪个方法版本。

动态绑定的过程通常涉及以下步骤:

  1. 方法查找:当调用一个方法时,Java虚拟机首先在当前对象的类中查找该方法。
  2. 方法执行:如果找到了该方法,JVM执行该方法。如果方法被覆盖,JVM执行子类中的版本。
  3. 类型检查:如果当前对象是子类类型,而方法在父类中被声明为不可访问(如private),JVM会抛出IllegalAccessError异常。

动态绑定确保了对象的行为是基于对象的实际类型,而不是引用类型。这是Java实现多态性的关键机制之一。在编译时,编译器只能确定方法调用的静态类型,而实际执行的方法版本是在运行时由JVM(真正运行java程序的地方)根据对象的实际类型确定的。

我们先来了解一下静态类型和动态类型

静态类型和动态类型,它们描述了类型检查是在编译时还是运行时进行的。

静态类型指的是在编译时期就已经确定的数据类型。在静态类型语言中,所有的类型检查和类型绑定都是在编译时完成的。这意味着你在编写代码时就必须明确每个变量的类型,并且在编译过程中,编译器会检查这些类型的正确性。Java 是一种静态类型语言,因为在 Java 中,所有的变量在使用之前都必须声明其类型。

例如,在 Java 中,当你声明一个变量并分配一个值时,如:

​
int x = 10;

​

在这里,x 的类型在编译时就已经确定为 int 类型。如果你尝试将一个非 int 类型的值赋给 x,编译器会报错,因为类型不匹配。

动态类型则相反,它指的是在运行时才确定数据类型的语言。在动态类型语言中,你不需要在编写代码时声明变量的类型;类型检查和绑定是在程序运行时由解释器或虚拟机动态完成的。

在 Java 中,虽然基本数据类型(如 intdoublechar 等)是静态类型的,但对象引用(如类实例)是动态类型的。这意味着当你通过一个对象引用调用一个方法时,实际执行的方法版本是在运行时由 Java 虚拟机根据对象的实际类型确定的,这就是上面提到的动态绑定。

总结来说,静态类型是指类型检查在编译时进行,而动态类型是指类型检查在运行时进行。Java 中的方法调用类型安全检查大部分是在编译时完成的,但方法的实际执行版本却在运行时根据对象的实际类型确定,这就是 Java 支持多态性的一个重要机制。

这时,我们就可以引出多态的概念了。 

多态

多态的实现条件

  1. 继承(Inheritance): 子类必须继承自父类。
  2. 覆盖(Overriding): 子类必须重新定义父类中的方法。
  3. 接口(Interfaces): 类也可以实现接口,接口中定义的方法也可以被认为是一种多态。

多态的类型

     1.编译时多态: 也称为静态多态,是通过方法重载实现的。在编译时就已经确定具体调用哪个方法。

这个不用过多解释,因为重载的方法的参数类型不同,在编译时编译器就可以分辨出该调用哪个方法。

      2.运行时多态: 也称为动态多态,是通过继承和接口实现的。在运行时确定具体调用哪个方法。

例:


class Animal {
    void sound() {
        System.out.println("Animal makes a sound");
    }
}

class Dog extends Animal {
    void sound() {
        System.out.println("Dog says: Bow Wow");
    }
}

class Test {
    public static void main(String args[]) {
        Animal a;
        a = new Dog();  // Dog对象被向上转型为Animal类型
        a.sound();  // 调用的是Dog类的sound方法
    }
}

​

在这个例子中,Animal 类的 sound 方法被 Dog 类覆盖。当我们创建一个 Dog 对象并将其赋值给一个 Animal 类型的引用时,我们实际上是在使用动态绑定。最终调用的是 Dog 类的 sound 方法,而不是 Animal 类的 sound 方法。

变量调用与方法调用的多态

以上只是对象调用成员方法的多态判断,还有对象调用成员变量的多态判断。

Animal a = new Dog();
a.sex;
a.eat();

记住口诀:
变量调用:编译看左边,运行也看左边。
方法调用:编译看左边,运行看右边。

1.调用成员变量:编译看左边,运行也看左边
编译看左边:java编译代码的时候,会看左边的父类中有没有这个变量,如果有编译成功,如果没有编译失败。
运行也看左边:java运行代码的时候,实际获取的就是左边父类中成员变量的值
2.调用成员方法:编译看左边,运行看右边
编译看左边:java编译代码的时候,会看左边的父类中有没有这个方法,如果有编译成功,如果没有编译失败。
运行看右边:java运行代码的时候,实际上运行的是子类中的方法。

第二种就是我们上面说的运行时多态了。

多态的弊端

不能使用子类的特有功能(这个弊端的解决方法就是利用强制类型转换

以上结论皆来源于执行代码时底层的内存变化,感兴趣的可以去了解了解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值