【JAVA】动态绑定与静态绑定

什么是动态绑定和静态绑定?


绑定(Build)

先说什么是绑定

绑定(Binding)就是:方法调用方法实现之间建立联系的过程。

简单来说但你调用a.eat() 这个方法的时候,程序要知道你具体调用的是哪段代码 这个寻找具体调用的段代码的过程,就叫做绑定


一个程序想要”跑”起来,需要经过两个阶段,先编译运行

在编译过程中就确认了具体用哪个方法就叫静态绑定 在运行过程中才确认用哪个方法就叫动态绑定

静态绑定

程序在编译阶段就能确定使用那个方法

类型: 通过下面关键词修饰的方法是静态的,子类无法对父类的方法进行重写

  • static 方法,静态方法
  • private 方法,私有方法
  • final 方法,被 final 修饰不能被重写
  • 构造方法
  • 通过 super().xxx 调用的方法

为什么这些是静态绑定? 因为它们都有一个共同点:不能被子类重写,或者调用方式跟对象的实际类型无关。既然不存在”多个版本”的可能性,编译器自然在编译时就能拍板。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Animal {
public static void info() { // 静态方法
System.out.println("我是动物");
}
}

class Dog extends Animal {
public static void info() { // 注意:这不是重写,是"隐藏"
System.out.println("我是狗");
}
}

public class Main {
public static void main(String[] args) {
Animal a = new Dog();
a.info(); // 输出:我是动物 ← 看引用类型,不看实际对象!
}
}

像程序一样思考 >这里的 a.info() 方法被调用了, >先看引用类型是 Animal, >进入 Animal 看 info 方法, >哦,发现被 static 修饰所以是静态方法.直接确定了就是这个函数 >用这个函数返回 我是动物

注意!!

在 java 中成员变量也是静态绑定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Animal {
String name = "动物";
}

class Dog extends Animal {
String name = "狗";
}

public class Main {
public static void main(String[] args) {
Animal a = new Dog();
System.out.println(a.name); // 输出:动物 ← 不是"狗"!
}
}

输出动物,而不是狗,因为成员变量是静态绑定 >这就告诉我们在写 java 的时候,最好不要在子类和父类中定义相同的成员变量.容易引发误解

又一个注意!

成员变量确实是静态绑定的,但是如果只有父类有这个成员变量的话,那自然也就调用父类这个成员变量, 比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
}

class Dog extends Animal {
public Dog(String name) {
super.(name);
}
}

public class Main {
public static void main(String[] args) {
Animal a = new Dog(Xiao);
System.out.println(a.name); // 输出:Xiao
}
}

在这个代码中创建了一个叫 a 引用为Animal 的 Dog 对象


动态绑定

程序在运行阶段才能确定使用那个方法

编译时只能确定方法签名,到运行时才能根据对象的实际类型决定调用哪个版本

类型:

  • 除了静态绑定之外的,就是动态绑定
  • 成员函数默认就是动态绑定.这也表现出 java 的多样性,是多态的bedrock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Animal {
public void eat() { // 普通实例方法
System.out.println("动物在吃");
}
}

class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗在啃骨头");
}
}

public class Main {
public static void main(String[] args) {
Animal a = new Dog();
a.eat(); // 输出:狗在啃骨头 ← 看实际对象,不看引用类型!
}
}

像程序一样思考: > 编译时: > 要调用a.eat(); 方法, 我进看看 a 的引用类型看看这个方法到底存不存在 > a 的引用类型是 Animal 他确实有 eat 方法. ✅编译通过, 但不决定调用哪个版本

运行时: JVM 检查 a 实际指向的是 Dog 对象,去 Dog 类里找 eat(),找到了就执行 Dog.eat()

这种”运行时根据真实类型查找方法”的机制,就是动态绑定也叫虚方法调用(Virtual Method Invocation)


总结:

静态绑定

  • 在编译过程中就确认了具体用哪个方法

动态绑定

  • 在运行过程中才确认用哪个方法