多【多种】态【状态】,方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现
1) 方法的多态(重写和重载就体现多态)
2)对象的多态 (核心,困难,重点)
● 方法的多态案例
public class PloyMethod {
public static void main(String[] args) {
//方法重载体现多态
A a = new A();
//这里我们传入不同的参数,就会调用不同 sum 方法,就体现多态
System.out.println(a.sum(10, 20));
System.out.println(a.sum(10, 20, 30));
//方法重写体现多态
B b = new B();
a.say();
b.say();
}
}
class B { //父类
public void say() {
System.out.println("B say() 方法被调用...");
}
}
class A extends B {//子类
public int sum(int n1, int n2) {//和下面 sum 构成重载
return n1 + n2;
}
public int sum(int n1, int n2, int n3) {
return n1 + n2 + n3;
}
public void say() {
System.out.println("A say() 方法被调用...");
}
}
● 属性没有重写之说!属性的值看编译类型
public class PolyDetail02 {
public static void main(String[] args) {
//属性没有重写之说!属性的值看编译类型
Base base = new Sub();//向上转型
System.out.println(base.count);// ? 看编译类型 10
Sub sub = new Sub();
System.out.println(sub.count);//? 20
}
}
class Base { //父类
int count = 10;//属性
}
class Sub extends Base {//子类
int count = 20;//属性
}
● 对象的多态案例
(1)一个对象的编译类型和运行类型可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的.
(4)编译类型看定义时=号的左边,运行类型看=号的右边
Animal animal = new Dog(); 【animal编译类型是Animal,运行类型Dog】
animal = new Cat();【animal的运行类型变成了Cat,编译类型仍然是 Animal)
定义Animal类
public class Animal {
public void cry() {
System.out.println("Animal cry()动物在叫....");
}
}
定义Dog和Cat类继承分别Animal类
public class Dog extends Animal {
@Override
public void cry() {
System.out.println("Dog cry() 小狗汪汪叫...");
}
}
public class Cat extends Animal {
@Override
public void cry() {
System.out.println("Cat cry() 小猫喵喵叫...");
}
}
多态注意事项和细节讨论
◆ 多态的前提是:两个对象(类)存在继承关系
◆ 多态的向上转型
1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用名= new子类类型();
3)特点:编译类型看左边,运行类型看右边。可以调用父类中的所有成员(需遵守访问权限).不能调用子类中特有成员;
最终运行效果看子类的具体实现!
//向上转型:父类的引用指向了子类的对象
//语法: 父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
Object object = new Cat();//可以吗? 可以 Object也是 Cat 的父类
//(1)可以调用父类中的所有成员
//(2)不能调用子类中特有成员;
//(3)因为在编译阶段,能调用那些成员,是由编译类型来决定的
//animal.catchMouse();错误
//最终运行效果看子类的具体实现
//(4)最终运行效果看子类(运行类型)的具体实现, 即调用方法时,按照从子类(运行类型)开始查找方法
// 然后调用,规则我前面我们讲的方法调用规则一致。
animal.eat();//猫吃鱼..
animal.run();//跑
animal.show();//hello,你好
animal.sleep();//睡
◆ 多态向下转型
1)语法:子类类型 引用名=子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所有的成员
//多态的向下转型
//1)语法:子类类型引用名=(子类类型)父类引用;
//2)只能强转父类的引用,不能强转父类的对象
//3)要求父类的引用必须指向的是当前目标类型的对象
//4)当向下转型后,可以调用子类类型中所有的成员
Cat cat = (Cat) animal;
cat.catchMouse();
cat.eat();
instanceOf 比较操作符
instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型
public class PolyDetail03 {
public static void main(String[] args) {
BB bb = new BB();
System.out.println(bb instanceof BB);// true
System.out.println(bb instanceof AA);// true
//aa 编译类型 AA, 运行类型是 BB
//BB 是 AA 子类
AA aa = new BB();
System.out.println(aa instanceof AA);
System.out.println(aa instanceof BB);
Object obj = new Object();
System.out.println(obj instanceof AA);//false
String str = "hello";
//System.out.println(str instanceof AA);
System.out.println(str instanceof Object);//true
}
}
class AA {
} //父类
class BB extends AA {
}//子类
多态的应用
1) 多态数组
数组的定义类型为父类类型,里面保存的实际元素类型为子类类型
应用实例:现有一个继承结构如下:
要求创建 1 个 Person 对象、2 个 Student 对象和 2 个Teacher 对象, 统一放在数组中,并调用每个对象say 方法. 应用实例升级:如何调用子类特有的方法,比如
Teacher 有一个 teach , Student 有一个 study
怎么调用?
定义 Person 类
public class Person {//父类
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String say() {
return name + "\t" + age;//返回名字和年龄
}
}
定义 Student 继承 Person 类
public class Student extends Person {
private double score;
public Student(String name, int age,double score) {
super(name, age);
this.score = score;
}
public double getScore() {
return score;
}
public void setScore(double score) {
this.score = score;
}
//重写父类的say()
@Override
public String say() {
return "学生 "+super.say()+" score="+score;
}
//特有方法
public void study(){
System.out.println("学生 "+ getName()+" 正在学Java课程...");
}
}
定义 Teacher 继承 Person 类
public class Teacher extends Person{
private double salary;
public Teacher(String name, int age,double salary) {
super(name, age);
this.salary = salary;
}
public double getSalary() {
return salary;
}
public void setSalary(double sal) {
this.salary = sal;
}
//重写父类的say()
@Override
public String say() {
return "老师 "+super.say()+" sal="+salary;
}
public void teach(){
System.out.println("老师 "+ getName()+" 正在讲Java课程...");
}
}
定义 PloyArray 进行方法调用
public class PloyArray {
public static void main(String[] args) {
Person[] persons = new Person[5];
persons[0] = new Person("jack", 20);
persons[1] = new Student("marry", 18, 100);
persons[2] = new Student("smith", 19, 30.1);
persons[3] = new Teacher("scott", 30, 20000);
persons[4] = new Teacher("king", 50, 50000);
for (int i = 0; i < persons.length; i++) {
if (persons[i] instanceof Student){
// Student students = (Student)persons[i];
// students.study();
((Student)persons[i]).study();
}else if (persons[i] instanceof Teacher){
((Teacher)persons[i]).teach();
}else if (persons[i] instanceof Person){
}else {
System.out.println("你的类型有误,请自己检查");
}
persons[i].say();
System.out.println(persons[i].say());
}
}
}
2)多态参数
方法定义的形参类型为父类类型,实参类型允许为子类类型
定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法。
普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法
测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]
测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法
定义 Employee 类
public class Employee {
private String name;
private double salary;
public Employee(String name, double salary) {
this.name = name;
this.salary = salary;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getSalary() {
return salary;
}
public void setSalary(double salary) {
this.salary = salary;
}
public double getAnnual(){
return 12 * salary;
}
}
定义 Manager 继承 Employee
public class Manager extends Employee {
private double bonus;
public Manager(String name, double salary, double bonus) {
super(name, salary);
this.bonus = bonus;
}
public double getBonus() {
return bonus;
}
public void setBonus(double bonus) {
this.bonus = bonus;
}
public void Manager() {
System.out.println("经理 " + getName() + " is managing");
}
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
定义 Worker 继承 Employee
public class Worker extends Employee{
public Worker(String name, double salary) {
super(name, salary);
}
public void work(){
System.out.println("普通员工 "+getName()+" is working");
}
@Override
public double getAnnual() {//因为普通员工没有其他的收入,则直接调用父类方法
return super.getAnnual();
}
}
定义 PloyParameter 调用方法
public class PloyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom", 2500);
Manager milan = new Manager("milan", 5000, 20000);
PloyParameter ployParameter = new PloyParameter();
ployParameter.showEmpAnnual(tom);
ployParameter.tetWork(tom);
ployParameter.showEmpAnnual(milan);
ployParameter.tetWork(milan);
}
public void showEmpAnnual(Employee e) {
System.out.println(e.getAnnual());
}
public void tetWork(Employee e) {
if (e instanceof Worker) { //向下转型
((Worker) e).work();
} else if (e instanceof Manager) { //向下转型
((Manager) e).Manager();
}else {
System.out.println("不做处理");
}
}
}