
文章目录
- MC开发日记--Java基础(一):多态
- 前言
- 一、什么叫多态?
- 二、调用顺序:
- 1."is a"原则:
- 2.函数表
- 补充
前言
本系列的内容大都是源自《Java核心技术》第10版(以下称《核心技术》)以及菜鸟教程中的内容
以下是本篇文章正文内容,下面案例可供参考
多态发生在父类对象指向子类对象时,调用共有接口时,此时编译器会决定哪一个接口会被真正调用:换言之,当且仅当:
继承(inheritance)
覆写(override)
父对象指向子对象时会出现
用《核心技术》中的例子来进行说明:
//父类:Employee类
class Employee{
// 静态域
private static int nextId = 0;
// 实例域
private String name = "Employee";
private double salary = 0;
private Date hireDay = new Date(2020, 1, 1);
private int id = 0;
public Employee(String str, double sal, int year, int month, int day){
name = str;
salary = sal;
hireDay = new Date(year, month, day);
}
//返回雇员的薪水
public double getSalary(){return salary;}
}
//子类:Manager类
public class Manager extends Employee{ //A extends B表示A类继承了B类
private double bonus = 0; //子类中独有的属性
public void setBonus(double aBonus){ //子类中独有的方法
bonus = aBonus;
}
@Override
public double getSalary(){ //覆写父类中的getsalary方法
double baseSalary = super.getSalary();
System.out.println("Manager's salary");
return bonus + baseSalary;
}
public Manager(String aName, double aSal, int aYear, int aMonth, int aDay){
super(aName, aSal, aYear, aMonth, aDay);
}
}
public class EmployeeTest {
public static void main(String[] args){
//在这种情况下,Manager类只能算是“继承”了Employee,多态还没有发生
Manager manager = new Manager("Alice", 15000, 1999, 2, 21);
Employee employee = new Manager("Alice", 15000, 1999, 2, 21);
employee.getSalary();
}
}
可以看到,在Employee类和Manager类中都存在*getSalary()*方法,当我们以父类对象变量调用该方法时:
最终调用的结果为子类(Manager类)的对应方法,这里就可以看到,尽管允许将子类对象赋值给父类对象,但调用方法时,以该对象本身的类型为基准
这一节开始深入理解上面的现象,这也有助于更好地理解和运用多态。在《核心技术》中指出,继承关系可以用"is a"进行描述,Manager一定是一个Employee,这种想法能够迅速理解继承的关系。根据"is a"原则,很容易就能说明为什么能用Employee对象变量指向Manager。
同时,由继承关系知,从抽象层次来看,显然Employee包含了Manager,但从属性、方法的数量上看,又可以看成Manager扩充了Employee:
从第一部分的结果来看,可以得到两个信息:
1. 对象本身的类型与对象变量的类型不一定非得相同(Manager类对象可以被Employee对象变量引用
2.上一条成立的条件是对象变量的类型是父类,而对象本身是子类
那么编译器如何知道父类对象变量调用有重载的成员函数时,到底该调用哪一个呢?
事实上,Java编译器维护被称为方法表的结构,该结构从当前对象本身的类型所拥有的成员函数开始构造,一直构造到基类。当函数调用时,编译器自底向上地从当前类开始查找,一旦在当前类中找到符合要求的函数(符合要求指:与调用函数有相同的函数签名并且被标记为public),直接调用,否则到父类中继续该过程,如果始终没有找到则抛出异常。
例如例子中的两个类在方法表中可以看作有这样的结构:
class Employee:
getSalary() -> Employee.getSalary(),
...
class Manager:
getSalary() -> Manager.getSalary(),
...
然后自底向上地进行查找,就能够得到刚才的结果。
补充
函数签名
根据《核心概念》,在Java中,签名被定义为由方法名和参数列表组成的,方法的唯一标识符。注意,返回类型并不算在函数签名中。如果在子类中定义了父类中的函数,那么该签名会覆写父类中的签名。
@Override注解
@Override并不是必要的,然而它可以帮助编译器检查错误。当方法前面用@Override注解后,父类中必须有该方法以供覆写否则会报错。而没有@Override时,显然即使父类中没有该方法,编译器也仅仅会认为这是一个普通函数,尽管程序正常运行,但却丢失了重载的性质。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)