📚 目录
前言:
面向对象编程有三大核心特性:封装、继承、多态。其中多态是 思想的精髓,它让程序具备更强的灵活性和可扩展性 ------ 允许不同子类对象以统一的父类形式被调用,却能执行各自独特的逻辑。
1. 什么是多态?
字面意思:一个事物多种形态。
具体点:就是不同的对象去完成时展现出不同的形态。

通俗来讲:我们通过狗和猫的对象去访问同一个东西的时候,狗去访问的时候会汪汪叫,而用猫去访问的时候就会变成喵喵叫。
[🔙 返回目录](#🔙 返回目录)
2. 实现多态的前提
想要实现多态的前言条件:
什么是重写?
什么是向上转型和向下转型?
什么是动态绑定?
在知道这三者的基础上,我们就能够实现多态。
[🔙 返回目录](#🔙 返回目录)
2.1重写
重写重新,顾名思义就是重新对一个方法进行写。(只有方法能够重写)!!!

想必大家在学校学习的时候都经历过重写。
重写可以理解为:在抄一篇古诗,古诗名、内容、作者不变,但是字迹是变化的。
Java中可以理解为:返回值和形参不能改变,也就是外壳不发生改变。核心在与重写方法的内部的结构。
多态是在继承的基础上开展的。
注意:
被(private、final、static)修饰的父类方法以及父类的构造方法不能被子类重写。
多态中重写的方法的返回值可以不同,但是必须是父子关系,才能进行重写。
父类:
java
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public Animal eat() {
System.out.println(name+"正在吃饭...");
return null;
}
}
子类:
java
public class Dog extends Animal{
public int age;
public Dog(String name) {
super(name);
}
@Override//代表这个方法被重写
public Dog eat() {//返回值不同
System.out.println(name+"正在吃狗粮...");
return null;
}
}
** @Override代表当前方法被重写。
重写的方法的修饰访问限定符要大于等于被重写的方法。(private < 默认的 < protected < public)但是被private修饰的不能被重写。**
父类:
java
public class Animal {
void eat() {
//...
}
}
子类:
java
public class Cat extends Animal{
@Override
public void eat() {
//...
}
}
java
public class Cat extends Animal{
@Override
protected void eat() {
//...
}
}
我们可以认为重写是多态的一种多形态的表现。
[🔙 返回目录](#🔙 返回目录)
2.2向上转型和向下转型
知道完重写,就可以开始多态的第二步。------>向上转型和向下转型。
向上转型:让父类引用 子类 的对象
形式:
父类:
java
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name+"正在吃饭...");
}
}
子类:
java
public class Cat extends Animal{
public Cat(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name+"正在吃猫粮...");
}
}
测试方法:当然我们也能把两个对象先创建出来再进行让父类引用子类。
java
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("咪咪");
}
}
当然我们能从结果上面看到:结果是子类的结果,而不是父类。

注意:
我们在向上转型的过程中只能调用父类自己原本有的方法,而不是子类有父类没有的方法;否则编译器会发生报错。
例如我们在子类中增加了一个方法。

当父类的方法的返回类型为自己的类型的时候,子类的返回值也可以是子类本身。
父类:
java
public class Animal {
public Animal eat() {//返回值为自己类本身
System.out.println(name+"正在吃饭...");
return null;
}
}
子类:
java
public class Cat extends Animal{
@Override
public Cat eat() {//返回值为子类的类型
System.out.println(name+"正在吃猫粮...");
return null;
}
}
注意:
只有两类为父子关系的时候才能这么写,否则编译器会报错。
向下转型:子类的对象 引用 父类的对象
注意:引用的时候需要强制类型转换:父类范围比子类大
** 将⼀个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法。**
例如:
java
class Animal {
public void sleep() {
// 父类方法
System.out.println("动物正在睡觉觉...");
}
}
class Cat extends Animal {
public void eat() {
// 子类独有方法
System.out.println("猫正在猫粮...");
}
}
public class Test {
public static void main(String[] args) {
// 情况1:直接创建Cat对象(子类引用)
Cat cat1 = new Cat();
cat1.sleep(); // 能调父类方法
cat1.eat(); // 能调子类独有方法
// 情况2:向上转型(父类引用)
Animal animal = new Cat();
animal.sleep(); // 能调父类方法
// animal.catchMouse(); 编译报错!编译器认为Animal没有这个方法
// 情况3:向下转型后(还原子类引用)
Cat cat2 = (Cat) animal;
cat2.eat(); // 能调子类独有方法
}
}
注意:
向下转型不安全(比如猫可以是动物,但是动物不一定是猫)!!!
向下转型只是讲动物还原成猫(而它的本质不是猫)!
我们一般情况下对向下转型的使用频率较低。
向下转型不安全,而Java官方给我们提供了关键字:instanceof ;来帮助我们检测向下转型是否成功。
例如:
java
public class Test {
public static void main(String[] args) {
Animal animal = new Cat("咪咪");
Cat cat = (Cat)animal;//向下转型
if(animal instanceof Cat) {
cat.sleep();
} else {
return;
}
}
}
** 如果animal不是Cat的父类编译器就会报错。**
[🔙 返回目录](#🔙 返回目录)
2.3动态绑定
字面意思:在运行的时候会进行绑定。
Java中是什么意思呢?
父类:
java
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name+"正在吃饭...");
}
}
子类:
java
public class Dog extends Animal{
public int age;
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name+"正在吃狗粮...");
}
}
创建一个测试类:
java
public class Test {
public static void main(String[] args) {
Animal animal = new Dog("旺财");
animal.eat();
}
}
我们掉到cmd界面来看:

** 在编译的时候编译器认为调用的是Animal这个类中的eat方法,而实际情况下调用的是Dog这个类中的方法。
在代码运行过程中对Dog类中eat方法进行绑定,这就叫动态绑定。**
[🔙 返回目录](#🔙 返回目录)
3. 多态的实现
知道了重写、向上向下转型、动态绑定:我们就能知道多态这个概念了。
实现多态:
父类:
java
public class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(name+"正在吃饭...");
}
}
子类:
java
public class Dog extends Animal{
public int age;
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(name+"正在吃狗粮...");
}
}
多态的实现:
java
public class Test {
public static void main(String[] args) {
Animal animal = new Dog("旺财");
animal.eat();
Animal animal1 = new Cat("咪咪");
animal1.eat();
}
}
通过不同的对象去访问同一个方法,得到的结果是不同的。

[🔙 返回目录](#🔙 返回目录)
4. 多态的好处
优点:
降低代码的圈复杂度。
拓展能力变强了。
圈复杂度:举个例子(我们创建了很多个家庭动作类型)

java
public class Test1 {
public static void main(String[] args) {
Father father = new Father();
Me me = new Me();
Sister sister = new Sister();
Mother mother = new Mother();
String[] doing = {"father","me","mother","sister"};
if(doing.equals("father")) {
father.toDo();
}else if(doing.equals("me")) {
me.toDo();
}else if(doing.equals("mother")) {
mother.toDo();
}else if(doing.equals("sister")) {
sister.toDo();
}
}
}
有很多if语句就会一圈套一圈,非常复杂。
而我们多态就能这么来做。

一共加起来才这几行代码。大大美化了我们的书写风格,显得非常高大上。
注意:
在我们多态中避免使用重名的方法:
父类:
java
public class Do {
public Do() {
func();
}
public void func() {
System.out.println("Do");
}
}
子类:
java
public class Me extends Do{
public int a = 10;
@Override
public void func() {
System.out.println("Me:"+a);
}
}
调用:
java
public class Test1 {
public static void main(String[] args) {
Do doing = new Me();
}
}

我们发现a的值为0。
** 原因:优先完成父类的构造方法,父类构造方法中调用了与子类同名的方法,他们构成了多态,进入到子类func方法中,父类与子类构造方法还没走完,子类的实例成员变量a还没有进行赋值,此时a的值就为默认值0。**
[🔙 返回目录](#🔙 返回目录)