基础准备
我们先来创建2个类,分别是Employee和Manager类,Manager继承Employee
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public Employee() {
}
public double getSalary() {
return salary;
}
}
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary) {
super(name, salary);
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
@Override
public double getSalary() {
return super.getSalary() + bonus;
}
}
下面的说明将会以这2个类作为例子
多态
在Java程序设计语言中,对象变量是多态的(polymorphic.)。一个Employee类型的变量
既可以引用一个Employee类型的对象,也可以引用Employee类的任何一个子类的对象。
public void t1(){
// 这是Manager对象
Manager ttpfx = new Manager("ttpfx", 12000);
// 可以使用Employee进行引用
Employee employee = ttpfx;
}
编译类型
// 这是Manager对象
Manager ttpfx = new Manager("ttpfx", 12000);
// 可以使用Employee进行引用
Employee employee = ttpfx;
看上面代码,对于ttpfx,他的编译类型就是Manager,而emplyee的编译类型就是Employee。简单一点说,我们在定义变量时候,变量左边的数据类型就是它的编译类型
运行类型
// 这是Manager对象
Manager ttpfx = new Manager("ttpfx", 12000);
// 可以使用Employee进行引用
Employee employee = ttpfx;
System.out.println(ttpfx.getClass());
System.out.println(employee.getClass());
还是对于上面这段代码,ttpfx的运行类型是Manager,而employee运行类型也为Manager。我们可以使用对象.getClass()来查看一个对象的实际运行类型,该方法就会返回实际类的全路径,上面代码输出如下
class com.ttpfx.Manager
class com.ttpfx.Manager
对象的运行类型和初始化有关,说白了就是new关键字后面的就是该对象的运行类型
强转类型转换
将一个类型强制转换成另外一个类型的过程称为强制类型转换。Java程序设计语言为强制类型转换提供了一种特殊的表示法。例如
double x = 3.14;
int a = (int) x
上面代码就将小数强制转换为了整数。
但是对于对象的强转有一点不同,我们只能将对象强转为对象的实际的运行类型或者对象运行类型的父类。
Employee[] employees = new Employee[2];
employees[0] = new Employee("tom", 10000);
employees[1] = new Manager("ttpfx", 12000);
看上面代码,我创建了一个Employee的数组,数组第二个元素是Manager,可以发现我现在并没有设置Manager的bonus(奖金),但是现在出现了一个问题,employee[1]的编译类型是Employee,并没有setBonus()方法,于是我们就需要将employee强转为Manager,下面代码就将设置Manager的bonus
((Manager) employees[1]).setBonus(60000);
对于一个类型能否成功进行转换可以使用instanceof进行判断
if (employees[1] instanceof Manager) {
((Manager) employees[1]).setBonus(60000);
}
如果可以转换返回true,否则返回false。其实就是判断是不是某个类或者某个对象的子类
动态绑定
以上面的代码为基础,编写以下代码
@Test
public void t2() {
Employee[] employees = new Employee[2];
employees[0] = new Employee("tom", 10000);
employees[1] = new Manager("ttpfx", 12000);
((Manager) employees[1]).setBonus(60000);
for (Employee employee : employees) {
System.out.println(employee.getSalary());
}
}
可以发现,我们使用foreach循环输出了数组内所有员工的薪水,在理想情况下,employee[0]应该输出10000,而employee[1]应该输出72000才正确,下面来查看一下。代码输出如下
10000.0
72000.0
可以发现也确实是这样的。这其实就是动态绑定,employee在调用getSalary方法时知道当前employee的实际运行类型,然后会在该类里面寻找getSalary,找到就直接调用,如果找不到那么再去父类进行寻找。因为employee[1]的实际运行类型是Manager,里面有getSalary方法,会返回salary+bonus的和,所以就不会调用父类(Employee)的getSalary方法了。
总结
其实上面这些东西说白了都是一个东西,就是对象调用方法会根据实际运行类型来开始查找方法进行调用,找不到再去父类查找。父类可以引用子类对象,我们只能将对象强转为对象的实际的运行类型或者对象运行类型的父类。