多态:一个对象变量可以指示多种实际类型。(通常情况下都是某个实际类及其子类类型的对象变量,子类引用可以赋值给超类变量,但是反过来不可以,即使强制类型转换可以通过编译,运行时也会报错。)
动态绑定:一个对象变量在运行过程中可以自动寻找合适的方法执行。
执行过程:
- 编译器首先查看对象的类型和调用的方法名。此时可能存在多个名称相同但是参数不同的方法,编译器会列举出对象类及其超类中所有同名的方法,此为可能被调用的候选方法。
- 编译器确定调用方法的提供参数类型,如果在候选方法中有符合条件的方法,就直接选择该方法(重载解析)。如果没有找到符合要求的方法,编译器会找通过类型转换可以满足要求的方法,但是如果有多个类型转换后可以满足要求的方法,编译器会报错。
- 如果程序采用动态解析方法,那个虚拟机必须调用变量实际类型对应的方法,如果实际类型不存在该方法,那么就寻找其超类中对应的方法,以此类推。为了减少方法搜索的时间,虚拟机预先为每个类定义了一个方法表列出方法方法签名和调用的实际方法,每次调用的时候只需查找方法表,而不用每次都要到每个类中寻找。完成方法执行。
请看下面的例子,Manager类为Employee的子类,并且Manager中定重新定义了getSalary方法,在执行过程中变量e会根据其实际类型调用正确的方法。
ManagerTest.class
public class ManagerTest {
public static void main(String[] args) {
var boss = new Manager("Car Craker", 80000, 1987, 12, 5);
boss.setBonus(5000);
var staffs = new Employee[3];
staffs[0] = boss;
staffs[1] = new Employee("Harry Haker", 50000, 1987, 12, 5);
staffs[2] = new Employee("Tommy Testter", 40000, 1987, 12, 5);
for(Employee e: staffs) {
System.out.println("name="+e.getName()+", salary="+e.getSalary()+", hireDay="+e.getHireDate());
}
}
}
Manager.java
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, int year, int month, int day) {
super(name, salary, year, month, day);
bonus = 0;
}
/**
* 设置bonus
*/
public void setBonus(double b) {
bonus = b;
}
/**
* 获取salary
*/
public double getSalary() {
double baseSalary = super.getSalary();
return baseSalary+bonus;
}
}
Employee.java
import java.time.*;
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
public Employee(String n, double s, int year, int month, int day) {
name = n;
salary = s;
hireDay = LocalDate.of(year, month, day);
}
public String getName() {
return name;
}
public double getSalary() {
return salary;
}
public LocalDate getHireDate() {
return hireDay;
}
public void raiseSalary(double byPercent) {
double raise = salary * byPercent/100;
salary += raise;
}
}