一、为什么需要多态

//传统方法 若动物种类 和 食物种类过多 ,将会构成过多的重载,过于繁琐,不利于维护和管理
现在问题是代码的复用性不高
而使用多态可以使用我们代码的复用性
二、基本介绍
方法或对象具有多种形态,是面向对象的第三大特征,多态是建立在封装和继承基础上的
多态的具体体现
1、方法的多态
方法的重写和重载就体现了多态
package com.hspedu.poly_;
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() 方法被调用...");
}
}
2、对象的多态(核心,难点,重点)
1)一个对象的 编译类型 可以和 运行类型 不一致
(new 的是 运行类型 ,定义对象名 的是 编译类型)
Animal animal = new Dog();//对象animal 编译类型是Animal,运行类型是 Dog
//父类(Animal)的引用可以指向子类(Dog)的一个对象。
2)编译类型在定义对象的时候,就确定了,不能改变
3)运行类型是可以改变的
Animal animal = new Dog();//对象animal 编译类型是Animal,运行类型是 Dog
animal = new Cat();//此时animal的运行类型变成了Cat,编译类型仍然是Animal
4)定义对象时,等号左边是编译类型,等号右边是运行类型
三、快速入门
package com.lmdedu.Poly.objectPoly;
public class PolyObject {
public static void main(String[] args) {
//体验对象多态特点
//animal编译类型是Animal,运行类型是Dog
Animal animal = new Dog();
//因为运行时,这时即执行到该行时,animal的运行类型是Dog,所以会执行Dog类的cry()方法
animal.cry();//狗叫
//animal编译类型是Animal,运行类型是Cat,所以会执行Cat类的cry()方法
animal = new Cat();//赋值后,指向发生变化
animal.cry();
}
}

使用多态机制解决前面提到的主人喂食问题
//使用多态机制,统一的管理主人喂食问题,
//animal的编译类型是Animal,可以指向(接受)Animal子类的对象,
//food的编译类型是Food,可以指向(接受)Food子类的对象,
public void feed(Animal animal,Food food){
System.out.println("主人" + name + "给" + animal.getName() + "喂食" + food.getName());
}
//传统方法
// //完成主人给小狗喂 骨头
// public void feed(Dog dog,Bone bone){
// System.out.println("主人" + name + "给" + dog.getName() + "喂食" + bone.getName());
// }
// //主人给小猫喂 鱼
// public void feed(Cat cat,Fish fish){
// System.out.println("主人" + name + "给" + cat.getName() + "喂食" + fish.getName());
// }
//添加给小猪喂米饭
Animal pig = new Pig("小花猪");
Food rice = new Rice("米饭");
System.out.println("=====================");
tom.feed(pig, rice);
四、多态的注意事项
多态的前提是:两个对象(类)存在继承关系
1、多态的向上转型
1)本质:父类的引用 指向了 子类的对象
2)语法:父类类型 引用名 = new 子类类型();
3)特点:编译类型看左边,运行类型看右边
可以调用父类中的所有成员(属性和方法)(需遵守访问权限)
但是不能调用子类中特有成员(属性和方法)
因为在编译阶段,能调用哪些成员,是由编译类型决定的
最终运行效果看子类的具体实现,因为在运行阶段,是由运行类型决定的
编译看父类,运行看子类
2、多态的向下转型
目的:为了解决 上面说的 不能调用子类中特有成员
1)语法 子类类型 引用名 = (子类类型) 父类引用;(将一个父类引用强制转换为子类类型 的引用)
//老师希望可以调用Cat的catchMouse方法,因为此方法为子类的特有成员
//于是使用多态的向下转型
//语法 子类类型 引用名 = (子类类型) 父类引用;
//将编译类型由 Animal强转为 Cat
//此时编译类型为 Cat,运行类型为 Cat
Cat cat = (Cat) animal;
cat.catchMouse();
2)只能强转父类的引用,不能强转父类的对象(只能改变指向的类型,不能改变存储空间数 据)
3)要求 父类的引用 必须指向的是 当前目标类型的对象
//要求父类的引用 必须是指向的是 当前目标类型的对象
//由上面Animal animal = new Cat();知道 animal本来就是指向 Cat的,所以可以强转为
//但若将 一个运行类型为 Cat的 animal 强转为一个Dog,将报错ClassCastException
Dog dog = (Dog) animal;
4)当 向下转型后,就可以调用 子类类型的 所有成员了
3、属性没有重写之说,属性的值看编译类型
4、instanceOf比较操作符
instanceOf比较操作符用于判断对象的类型(运行类型) 是否为 XX类型 或XX类型的子类型
用法: 对象 instanceOf 类型
Object object = new Object();
System.out.println(obj instanceOf AA);//其实就是问 obj是AA或者AA的子类吗
//因为Obj的运行类型是 Object,不是AA或者AA的子类,所以为false
五、练习


输出 20 20 true 10 20
六、java的动态绑定机制(重点)
定义
1、当调用对象的方法时,该方法会和对象的运行类型绑定(从运行类型开始找能否调用该方法,若没有就继续往上找)
2、当调用对象的属性时,没有动态绑定机制,遵守哪里声明,哪里使用

System.out.println(a.sum());
若此时将 B类中的sum()方法注销,那么a.sum()就会去找A的sum()方法,但是父类中的sum()方法有一个getI()方法,子类和父类都有getI()方法,于是就引出了 动态绑定机制

因为a的运行类型是 子类B,所以 根据动态绑定机制,于是就调用了子类B的getI()方法
因此父类中的sum()方法就是return 的 20+10
System.out.println(a.sum1());
此时注销B类中的sum1()方法,则会去调用A类的sum1()方法,return i+10;这里的 i 是属性,而属性没有动态绑定机制,哪里声明,哪里调用,因此这里的i调用的是B类的i = 10;于是10+10.
0314_韩顺平Java_动态绑定机制_哔哩哔哩_bilibili
七、多态数组
定义: 数组的定义类型 为 父类类型,里面保存的实际元素类型 为 子类类型

package com.lmdedu.Poly.PolyArray;
public class PolyArray {
public static void main(String[] args) {
//创建一个Person,2个Student 2个 Teacher,统一放在数组中,并调用每个对象的say()方法
//创建一个Person类型的数组
Person[] person = new Person[5];
person[0] = new Person("jack",20);
person[1] = new Student("marry",20,100);
person[2] = new Student("smith",19,30.1);
person[3] = new Teacher("scott",30,11000);
person[4] = new Teacher("king",50,21000);
//循环遍历多态数组,调用say()
for(int i = 0; i < person.length; i++){
//person[i],编译类型为Person,运行类型根绝实际情况由jvm机来判断
System.out.println(person[i].say());;//动态绑定机制
//调用子类特有的方法
if(person[i] instanceof Student){//instanceof用于判断运行类型
//(Student)person[i]将person[i]强转为Student
Student student = (Student)person[i];
(student).study();
//或者简洁一点((Student)person[i]).study();
} else if (person[i] instanceof Teacher) {
//(Teacher)person[i]将person[i]强转为Teacher类型
((Teacher)person[i]).teach();
}else if(person[i] instanceof Person){
}else{
System.out.println("你的类型有误");
}
}
}
}
八、多态参数

1、比如前面的主人喂食物问题,feed函数中 第一个参数可以为 Animal,也可以为Animal的子类Dog,Cat,第二个参数可以为Food,也可以为Food的子类 Bone,rice等
//调用子类的特有方法 需要进行强转,向下转型 ((子类类型)对象).调用的子类特有方法
package com.lmdedu.Poly.poly_parameter;
public class PolyParameter {
public static void main(String[] args) {
Worker tom = new Worker("tom",2500);
manager jack = new manager("jack", 5000,1100);
Test test = new Test();
test.showEmpAnnual(tom);
test.showEmpAnnual(jack);
test.testWork(tom);
test.testWork(jack);
}
}
class Test{
public void showEmpAnnual(Employee e){
//会动态绑定,传进来的e是什么类,就会调用对应的getAnnual()
System.out.println(e.getAnnual());
}
//调用子类的特有方法 需要进行强转,向下转型 ((子类类型)对象).调用的子类特有方法
//若为普通员工就调用work方法,若为经理, 则调用manager方法
public void testWork(Employee e){
if(e instanceof Worker){
//将编译类型强转为Worker
((Worker)e).work();
}else if(e instanceof manager){
((manager)e).manage();
}else{
System.out.println("输入类型有误");
}
}
}
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;
}
}
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 manage(){
System.out.println("经理" + getName() + "正在管理");
}
//重写获取年薪的方法
@Override
public double getAnnual() {
return super.getAnnual() + bonus;
}
}
class Worker extends Employee{
public Worker(String name, double salary) {
super(name, salary);
}
public void work(){
System.out.println("普通员工" + getName() + "正在工作");
}
//重写获取年薪的方法
@Override
public double getAnnual(){
//普通员工没有奖金,所以直接继承getAnnual()方法
return super.getAnnual();
}
}
本文介绍了Java中的多态概念,包括方法的重载和重写、对象的多态(编译类型与运行类型的关系)、动态绑定机制以及多态在数组和函数参数中的应用。通过实例展示了如何提高代码复用性和灵活性。
287

被折叠的 条评论
为什么被折叠?



