⛳️基本概念
同一种事物,在不同时刻表现不同的状态
比如:水果表现出来的不同形态有:香蕉、猕猴桃、苹果
再比如:猫、狗、猴子可以把它们都归类为动物,每种动物都要吃东西,所以我们可以设置他们都必须吃,但是每种动物的习性又不一样,那么我们就可以设置每个动物自己特有的功能,多态对象只能调用父类中定义的子类中的重写的功能,不能调用子类中特有的功能,这样就实现了代码的统一性。
⛳️多态存在的三个必要条件
1.要有继承(包括接口的实现)(前提条件)
2.要有重写(前提条件)
3.父类引用指向子类对象
当编译期类型是父类,运行期类型是子类时,被称为父类引用指向子类对象
class Animal{
......
}
class Cat extends Animal{
......
}
class Dog extends Animal {
......
}
//Animal的引用指向Cat的对象
Animal x = new Cat()
⛳️多态环境下的调用
1.针对非静态成员方法:编译看左边,运行看右边
2.针对静态方法:编译期间看左边,运行期间还是看左边
3.针对成员变量:编译期间看左边,运行期间还是看左边
案例:
定义一个抽象类父类Animal:
public abstract class Animal {
int age = 10;
public Animal(){
}
//定义一个抽象方法
public abstract void eat();
//定义一个成员方法
public void sleep(){
System.out.println("动物睡");
}
//静态方法,不能重写
public static void play(){
System.out.println("动物玩");
}
}
定义子类Dog继承Animal:
public class Dog extends Animal{
int age = 15;
public Dog(){
}
//重写Animal中的eat方法
@Override
public void eat() {
System.out.println("Dog重写Animalh中的eat()---狗吃骨头");
}
}
定义子类Cat继承Animal:
public class Cat extends Animal {
//重写Animal中的eat方法
@Override
public void eat() {
System.out.println("猫吃鱼");
}
}
定义子类Monkey继承Animal:
public class Monkey extends Animal{
//重写Animal中的eat方法
@Override
public void eat() {
System.out.println("猴子吃香蕉");
}
}
测试:
public class Test {
public static void main(String[] args) {
Dog xh = new Dog();
//编译期间 a.eat() a的类型是Animal类,调用的是抽象的eat()
//运行期间 a执向的是一个Dog对象,运行的是Dog中重写的eat()方法
Animal a = new Dog();
/*
针对非静态成员方法:编译看左边,运行看右边
*/
a.eat();//Dog重写Animalh中的eat()---狗吃骨头
//不是直接调用父类的sleep,而是找dog对象父类的sleep()
a.sleep();//动物睡
/*
针对静态方法:编译期间看左边,运行期间还是看左边
*/
a.play();//动物玩
/*
针对成员变量:编译期间看左边,运行期间还是看左边
*/
System.out.println(a.age);//10
}
}
注意:变量不存在被子类覆写这一说法,只有方法存在覆写。
⛳️方法参数具有多态性
方法参数多态性的好处:提高代码的扩展性
public class Test1 {
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
Monkey monkey = new Monkey();
Test3 t = new Test3();
t.feedAnimal(dog);
t.feedAnimal(cat);
t.feedAnimal(monkey);
}
/*
喂动物
*/
public void feedAnimal(Animal animal){
animal.eat();
}
}
⛳️向上转型和向下转型
在继承中,通过extends关键字,子类可以复用父类的功能,如果父类不能满足当前子类的需求,则子类可以通过重写父类中的方法来加以扩展,在这个过程中就存在多态的应用。
1.向上转型(自动转型:子继承父):可以把不同的子类对象都当作父类来看,进而屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一调用标准。
eg:父类Animal,子类dog 父类的引用指向子类对象:Animal dog = new dog();
说明:向上转型时,子类对象当成父类对象,只能调用父类的功能,如果子类重写了父类中声明过的方法,方法体执行的就是子类重写后的功能,但是此时对象是把自己看做是父类类型的,所以其他资源使用的还是父类型的。
eg:花木兰替父从军,大家都把花木兰看做她爸,但是实际从军的是花木兰,而且,花木兰只能做她爸能做的事,在军营里是不可以化妆的。
2.向下转型(强制转型:父类类型转为子类自己的类型):子类的引用的指向子类对象,过程中必须要采取到强制转型,之前向上转型过的子类对象仍然想执行子类的特有功能,所以需要重新恢复成子类对象。
Animal a = new dog();//向上转型,此时,a是Animal类型
Dog d = (Dog)p;//此时,把Animal类型的p转成小类型Dog 其实,相当于创建了一个子类对象一样,可以用父类的,也可以用自己的。
说明:向下转型时,是为了方便使用子类的特殊方法,也就是说当子类方法做了功能拓展,就可以直接使用子类功能。
eg:花木兰打仗结束,就不需要再看做是她爸了,就可以化妆了。
public class Test3 {
public static void main(String[] args) {
//向上转型
Animal dog = new Dog();
Cat cat = new Cat();
Monkey monkey = new Monkey();
Test3 t = new Test3();
t.feedAnimal(dog);
t.feedAnimal(cat);
t.feedAnimal(monkey);
}
/*
喂动物
*/
public void feedAnimal(Animal animal){
animal.eat();
//判断animal中实际传入的是什么
//父类引用 instanceof(是不是) 具体的子类类型
//instanceof 判断父类引用实际表示的对象
System.out.println(animal instanceof Dog);
if(animal instanceof Dog){
//向下转型
Dog dog = (Dog)animal;
dog.play();
}
}
}
优点:父类引用表示子类对象,提升程序的扩展性
缺点:父类类型不能调用子类中特有的方法