1.概念
对于同一行为,不同的子类对象有不同的表现。例如:动物吃东西,狗吃肉,猫吃鱼
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作,如图所示:
例子:比如电脑上的按键,在不同的情况下有不同的作用。比如我们按下 F1 键这个动作:
1.如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;
2.如果当前在 Word 下弹出的就是 Word 帮助;
3.在 Windows 下弹出的就是 Windows 帮助和支持。
4.同一个事件发生在不同的对象上会产生不同的结果。
2.分类
(1)静态多态(早绑定):在程序编译阶段,已经确定了方法的具体行为:已经确定了具体调用的方法-----典型代码:方法重载(2)动态多态(晚绑定):
3.实现多态条件
1. 必须要在继承体系下
2. 子类必须要对父类想要实现多态的方法进行重写---子类对父类的方法进行重写(对重写方法调用:只能通过基类的引用去调用重写的方法(在执行时,根据基类引用不同类的都西昂,就会调用对应类中被重写方法))
3.父类引用指向子类对象
注意:1子类对象可以看成是一个基类对象 猫是动物 狗是动物 结论:基类引用可以引用子类对象
class Shape {
void draw() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()");
}
}
class Triangle extends Shape {
void draw() {
System.out.println("Triangle.draw()");
}
}
多态的好处:可以使程序有良好的扩展,并可以对所有类的对象进行通用处理。
4.重写
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程
重写的规则:
1.子类在重写父类的方法时,一般必须与父类方法原型一致:修饰符 返回值类型 方法名(参数列表) 要完全一致
2.JDK7以后,被重写的方法返回值类型可以不同,但是必须是具有父子关系的
3.访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类方法被public修饰,则子类中重写该方法就不能声明为 protected
4.父类被static、private修饰的方法、构造方法都不能被重写。
5.子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
6.子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
7.重写的方法, 可以使用 @Override 注解来显式指定. 有了这个注解能帮我们进行一些合法性校验. 例如不小心将方法名字拼写错了 (比如写成 aet), 那么此时编译器就会发现父类中没有 aet 方法, 就会编译报错, 提示无法构成重写
public class Employee {
private String name;
private String address;
private int number;
public Employee(String name, String address, int number) {
System.out.println("Employee 构造函数");
this.name = name;
this.address = address;
this.number = number;
}
public void mailCheck() {
System.out.println("邮寄支票给: " + this.name
+ " " + this.address);
}
public String toString() {
return name + " " + address + " " + number;
}
public String getName() {
return name;
}
public String getAddress() {
return address;
}
public void setAddress(String newAddress) {
address = newAddress;
}
public int getNumber() {
return number;
}
}
public class Salary extends Employee
{
private double salary; // 全年工资
public Salary(String name, String address, int number, double salary) {
super(name, address, number);
setSalary(salary);
}
public void mailCheck() {
System.out.println("Salary 类的 mailCheck 方法 ");
System.out.println("邮寄支票给:" + getName()
+ " ,工资为:" + salary);
}
public double getSalary() {
return salary;
}
public void setSalary(double newSalary) {
if(newSalary >= 0.0) {
salary = newSalary;
}
}
public double computePay() {
System.out.println("计算工资,付给:" + getName());
return salary/52;
}
}
public class VirtualDemo {
public static void main(String [] args) {
Salary s = new Salary("员工 A", "北京", 3, 3600.00);
Employee e = new Salary("员工 B", "上海", 2, 2400.00);
System.out.println("使用 Salary 的引用调用 mailCheck -- ");
s.mailCheck();
System.out.println("\n使用 Employee 的引用调用 mailCheck--");
e.mailCheck();
}
}
运行结果:
Employee 构造函数
Employee 构造函数
使用 Salary 的引用调用 mailCheck –
Salary 类的 mailCheck 方法
邮寄支票给:员工 A ,工资为:3600.0
使用 Employee 的引用调用 mailCheck–
Salary 类的 mailCheck 方法
邮寄支票给:员工 B ,工资为:2400.0
5.向下转型和向上转型
向上转型:让基类的引用去引用子类的对象
- 直接赋值
- 方法传参
- 方法
向上转型的优点:让代码实现更简单灵活。
向上转型的缺陷:不能调用到子类特有的方法。
向下转型:向下转型是把父类对象转为子类对象
6.多态的优缺点
1.使得代码灵活
2.降低代码的圈复杂度
3.可扩展性强
4.接口性
5.可扩充性
6.可替换性
缺陷:1.代码运行效率降低 (自己写代码测试:可以测试代码总的运行时间 了解多态的底层实现原理)
代码的圈复杂度:是一种代码复杂度的衡量标准,也称为条件复杂度或循环复杂度,它可以用来衡量一个模块判定结构的复杂程度,数量上表现为独立现行路径条数,也可理解为覆盖所有的可能情况最少使用的测试用例数。简称 CC 。其符号为 VG 或是 M 。
圈复杂度 在 1976 年由 Thomas J. McCabe, Sr. 提出。
圈复杂度大说明程序代码的判断逻辑复杂,可能质量低且难于测试和维护。程序的可能错误和高的圈复杂度有着很大关系。