介绍
先来看看ai的解释
动态绑定机制是面向对象编程中的一个核心概念,特别是在Java这样的语言中。它允许在运行时根据对象的实际类型来决定调用哪个方法,而不是在编译时。这是多态性的关键特性之一。
在Java中,动态绑定是通过方法调用和方法执行的两个阶段来实现的:
1. **方法调用**:当调用一个对象的方法时,JVM(Java虚拟机)会查看对象的实际类型来决定哪个方法实现应该被调用。如果这个方法在对象的类中被重写,JVM会调用这个重写的方法。这个过程发生在运行时,因为它需要考虑对象的具体类型。
2. **方法执行**:一旦JVM确定了要调用的方法,它就会执行这个方法。在这个过程中,JVM会使用对象的实际类型来查找和执行正确的方法。
动态绑定的关键在于,它允许子类提供特定于自己类型的行为,即使是通过父类的引用来调用方法。这意味着,如果你有一个父类引用指向一个子类对象,当通过这个引用调用一个方法时,实际执行的是子类中定义的那个版本的方法(如果子类重写了这个方法的话)。
为了使动态绑定工作,Java使用了 invokevirtual 指令,这是Java字节码指令集的一部分。当JVM执行 invokevirtual 指令时,它会动态地确定应该调用哪个方法。
这里有一个简单的例子来说明动态绑定:
```java
class Animal {
void sound() {
System.out.println("Animal makes a sound");
}
}
class Dog extends Animal {
@Override
void sound() {
System.out.println("Dog barks");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Dog(); // 创建一个Dog对象的引用,但是引用类型是Animal
animal.sound(); // 这将调用Dog类中的sound方法,输出 "Dog barks"
}
}
```
在这个例子中,尽管 `animal` 的引用类型是 `Animal`,但是实际的对象是 `Dog` 类型。因此,当调用 `animal.sound()` 方法时,会动态绑定到 `Dog` 类的 `sound` 方法,输出 "Dog barks"。这就是动态绑定的魔力所在。
个人理解,这里还是离不开我们的编译类型和运行类型。动态绑定是绑定我们的运行类型,并且只对方法进行执行。对于属性则是在哪个类声明就用属性
实操代码
父类和子类
package hansunping;
//创建父类
public class person {
//简单的创建两个属性
private String name;
private int age;
//创建行为方法,方法中调用创建的say方法
public void haviours(){
say();
}
//创建方法
public void say(){
System.out.println("我是人类!!!");
}
}
//blog.csdn.net/YYniannian/article/details/126222600
//创建子类
class Student extends person {//如果没有指定访问修饰符,默认情况下该类具有包私有(package-priva
//te)访问级别,这意味着它只能在同一个包(package)内被访问。
//创建学生的属性
private int score;
//重写行为
// public void haviours(){
// say();
// }
//重写say方法
public void say(){
System.out.println("我是学生!!!");
}
}
分析,我们可以从这里看到父类有haviours,say方法。子类同样有haviour方法和say方法。并且haviour方法调用了say方法。
主类
我们在主类中
package hansunping;
public class demon1 {
public static void main(String[] args) {
person student = new Student();
//调用学生的say()方法,由于我们的编译类型是Person类,而运行类型是Student类,而动态绑定机制是绑定运行类型。
//所以,当我们调用say()方法时,是调用Student类的say()方法
student.haviours();//返回“我是学生”
//、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
//如果我们将student类的say()方法注销,他寻找student类的say方法无果后,会再向它的父类调用say方法。返回我是人类,效果等同于
}
}
person student = new Student();
person是编译类型。student是运行类型。我们的动态绑定就是与运行类型有关的。
在不注释子类的haviour方法时运行结果为
我是学生!!!
在注释之后结果 还是我是学生!!!
为什么呢?
因为子类的haviou方法不存在。那么根据我们的多态的继承寻找规则,我们就找到了父类的haviou方法。在父类的haviou方法中也是调用了say方法的。那么这个say方法是父类的吗?不是,这个方法应该是子类的。即运行类型的类方法。这里要弄懂编译类型和运行类型
接下来我们看另外一个
代码
package hansunping;
//创建父类
public class person {
//简单的创建两个属性
private String name;
private int age;
public int i =10;
public int sum() {
return getl()+10;
}
public int sum1() {
return i+10;
}
public int getl() {
return i;
}
}
//blog.csdn.net/YYniannian/article/details/126222600
//创建子类
class Student extends person {//如果没有指定访问修饰符,默认情况下该类具有包私有(package-priva
//te)访问级别,这意味着它只能在同一个包(package)内被访问。
//创建学生的属性
public int i =10;
public int sum() {
return getl()+20;
}
public int sum1() {
return i+10;
}
public int getl() {
return i;
}
}
主类
public class demon1 {
public static void main(String[] args) {
person student = new Student();
//调用学生的say()方法,由于我们的编译类型是Person类,而运行类型是Student类,而动态绑定机制是绑定运行类型。
//所以,当我们调用say()方法时,是调用Student类的say()方法
// student.haviours();//返回“我是学生”
System.out.println(student.sum());
System.out.println(student.sum1());
//、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
//如果我们将student类的say()方法注销,他寻找student类的say方法无果后,会再向它的父类调用say方法。返回我是人类,效果等同于
}
}
运行结果
30
20
好,现在我们同样注释掉子类的sum方法
结果
20
20
那么20是怎么来的,分析一下。student从子类中找不到sum方法,所以从父类找。父类有
public int sum() {
return getl()+10;
}
这个,那么这个get1()+10中的getl是父类的还是子类的方法呢?我将父类的
public int i =20;
i为20.如果是父类的话 那么应该是30了。动态绑定即允许环境还是在子类那里。而编译类型是个引用了,这个时候我们要看内存地址而不是指向的引用
我再在子类中添加;
public int getl() {
return i+1000;
}
结果为
1020
20
我们来验证下属性,我将子类的
public int sum() {
return i+30;
}
改成了i+30,发现结果为50
50
20.说明属性还是不变了