一、什么是多态
多态是指:同一操作(方法调用)作用于不同类型的对象上,会产生不同的执行结果。
简单来说:父类(或接口)的引用可以指向子类(或实现类)的实例,调用该引用的方法时,会根据对象的实际类型(而非引用类型)执行对应的方法实现,实现 "一种接口,多种实现"。
多态的底层支撑是动态绑定(后期绑定),编译时无法确定具体执行的方法,运行时才会完成方法的匹配与调用。
二、实现多态的三大必要条件
1、继承/实现关系:存在父类与子类的继承关系,或接口与实现类的实现关系(这是多态的基础,为 "引用复用" 提供前提)。
2、方法重写:子类(或实现类)必须重写父类(或接口)的非私有、非静态、非 final 方法(这是多态的核心,为 "不同结果" 提供保障)。
3、向上转型:使用父类(或接口)的引用变量指向子类(或实现类)的实例对象(格式:父类类型 引用 = 子类实例;,这是多态的表现形式,为 "统一调用" 提供载体)
三、多态的实现
我们接下来通过一些简单的代码来学习多态
Animal类
java
public class Animal {
public int age;
public String name;
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
public void eat(){
System.out.println(this.name+" 正在吃饭 ");
}
}
Dog类
java
public class Dog extends Animal{
public Dog(int age, String name) {
super(age, name);
}
@Override
public void eat() {
System.out.println(this.name+"吃骨头");
}
}
Cat类
java
public class Cat extends Animal{
public Cat(int age, String name) {
super(age, name);
}
@Override
public void eat() {
System.out.println(this.name+"正在吃鱼");
}
}
TestDemo类
java
public class TestDemo {
public static void main(String[] args) {
Dog dog=new Dog(18,"大黄");
dog.eat();
System.out.println("===================");
Animal animal=new Dog(18,"大黄");
animal.eat();
}
}
测试结果如下:

四、重写与重载的区别
1、重载
在同一个类中,方法名相同,但参数列表不同(参数个数、类型、顺序不同)的多个方法,称为方法重载。
重载的本质:是编译时多态(静态多态),编译器在编译阶段就能确定调用哪个方法。
java
public class Calculator {
// 重载1:两个int相加
public int add(int a, int b) {
return a + b;
}
// 重载2:三个int相加(参数个数不同)
public int add(int a, int b, int c) {
return a + b + c;
}
// 重载3:两个double相加(参数类型不同)
public double add(double a, double b) {
return a + b;
}
// 注意:仅返回值不同,不构成重载(编译报错)
// public double add(int a, int b) {
// return a + b;
// }
public static void main(String[] args) {
Calculator calc = new Calculator();
// 编译时确定调用add(int, int)
System.out.println(calc.add(1, 2)); // 输出3
// 编译时确定调用add(double, double)
System.out.println(calc.add(1.5, 2.5)); // 输出4.0
}
}

2、重写
子类继承父类后,对父类中非 private、非 final、非 static 的方法进行重新实现,方法名、参数列表、返回值类型(Java 5 + 支持协变返回值)完全相同,称为方法重写。
本质:是运行时多态(动态多态),程序运行时才确定调用子类还是父类的方法。
注意:子类重写的方法访问修饰符不能比父类更严格(如父类是 public,子类不能是 private);抛出的异常范围不能比父类更大。
Animal类
java
public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
Dog类
java
public class Dog extends Animal {
// 重写父类的makeSound方法
@Override // 注解:校验是否符合重写规则,可选但推荐加
public void makeSound() {
System.out.println("狗汪汪叫");
}
}
Test类
java
public class Test {
public static void main(String[] args) {
Animal animal1 = new Animal();
animal1.makeSound(); // 输出:动物发出声音
Animal animal2 = new Dog(); // 父类引用指向子类对象
animal2.makeSound(); // 运行时调用子类重写的方法,输出:狗汪汪叫
}
}

五、向上转型与向下转型
1、向上转型
向上转型的本质:将子类对象赋值给父类引用(从子类类型转换为父类类型)。
向上转型的特点:自动完成,无需显式声明;转型后只能调用父类中定义的方法(或子类重写的方法),无法调用子类特有方法。
下面我们举一个例子来为大家讲解一下向上转型:
Animal类
java
public class Animal {
public int age;
public String name;
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
public void eat(){
System.out.println("动物吃东西");
}
}
Cat类
java
public class Cat extends Animal{
public Cat(int age, String name) {
super(age, name);
}
@Override
public void eat() {
System.out.println(this.name+"正在吃猫粮");
}
}
TestDemo类
java
public class TestDemo {
public static void main(String[] args) {
Cat cat=new Cat(18,"咪咪");
cat.eat();
System.out.println("===========");
Animal animal=new Cat(3,"菲菲");//发生向上转型,将子类对象赋值给父类引用
animal.eat();
}
}
运行结果:

2、向下转型
向下转型的定义:将父类引用(实际指向子类对象)转换为子类类型。
向下转型的特点:必须显式声明(加(子类类型));如果父类引用没有指向对应的子类对象,转型会抛出ClassCastException 异常。
Animal类
java
public class Animal {
public int age;
public String name;
public Animal(int age, String name) {
this.age = age;
this.name = name;
}
public void eat(){
System.out.println("动物吃东西");
}
}
Cat类
java
public class Cat extends Animal{
public Cat(int age, String name) {
super(age, name);
}
@Override
public void eat() {
System.out.println(this.name+"正在吃猫粮");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
TestDemo类
java
public class TestDemo1 {
public static void main(String[] args) {
// 先向上转型
Animal animal = new Cat(2,"liu");
// 向下转型:显式声明,转换为Cat类型
Cat cat = (Cat) animal;
// 可以调用子类特有方法了
cat.catchMouse(); // 输出:猫抓老鼠
cat.eat(); // 输出:猫吃小鱼干
// 错误示例:父类引用指向父类对象,向下转型会报错
Animal animal2 = new Animal(2,"zw");
//Cat cat2 = (Cat) animal2; // 运行时抛出ClassCastException
}
}
运行结果如下:

六、instanceof关键字
instanceof 是 Java 的一个二元运算符(和+、-、==同级),
作用是:
判断左边的对象是否是右边类 / 接口的实例(或子类实例),返回值是boolean类型(true/false)。
简单说,它就是用来回答:这个对象是不是XX类型?(包括 XX 的子类)。
下面我们举一些关于instanceof关键字的例子,来帮助大家更好的掌握该关键字
java
class Animal {}
class Cat extends Animal {}
class Dog extends Animal {}
public class InstanceofDemo {
public static void main(String[] args) {
// 1. 子类对象 instanceof 父类 → true
Cat cat = new Cat();
System.out.println(cat instanceof Animal); // true(猫是动物)
// 2. 子类对象 instanceof 自身类 → true
System.out.println(cat instanceof Cat); // true(猫是猫)
// 3. 父类对象 instanceof 子类 → false
Animal animal1 = new Animal();
System.out.println(animal1 instanceof Cat); // false(普通动物不一定是猫)
// 4. 父类引用指向子类对象 instanceof 子类 → true
Animal animal2 = new Cat();
System.out.println(animal2 instanceof Cat); // true(这个动物实际是猫)
// 5. 父类引用指向其他子类 → false
Animal animal3 = new Dog();
System.out.println(animal3 instanceof Cat); // false(这个动物是狗,不是猫)
// 6. null instanceof 任何类型 → false(null没有对象类型)
Animal animal4 = null;
System.out.println(animal4 instanceof Animal); // false
}
}
七、多态的优缺点
首先我们要明确多态的定义,多态的本质是 "一个接口,多种实现",结合继承 / 实现 + 方法重写 + 向上转型实现,核心表现是 "父类引用指向子类对象,调用方法时执行子类重写的逻辑"。
1、多态的优点
①、提高代码的可扩展性(最核心)
java
// 父类
class Animal {
public void makeSound() {}
}
// 现有子类
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("喵喵");
}
}
// 新增子类(无需修改原有代码)
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("汪汪");
}
}
// 统一处理方法(新增子类后,此方法无需修改)
public static void play(Animal animal) {
animal.makeSound();
}
// 调用:新增Dog后,直接传参即可
play(new Cat()); // 喵喵
play(new Dog()); // 汪汪
②、提高代码的复用性和简洁性
可以用父类引用统一管理不同子类对象,避免编写大量重复的分支逻辑(比如不用写if (xxx instanceof Cat) {} else if (xxx instanceof Dog) {})。
2、多态的缺点
①、向上转型后无法直接调用子类特有方法
这是最直观的缺点:父类引用只能调用父类中定义的方法(或子类重写的方法),无法直接调用子类独有的方法,必须通过向下转型(且需instanceof校验)。
java
Animal animal = new Cat();
animal.makeSound(); // 可行(重写方法)
// animal.catchMouse(); // 报错(子类特有方法)
// 需转型:
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse(); // 可行,但增加了代码复杂度
}
②、不能重写静态方法和成员变量
多态仅针对非静态实例方法生效:静态方法属于类,不是对象,无法实现多态;成员变量也不会随子类重写而改变(调用时看引用类型)。
java
class Animal {
String type = "动物";
public static void test() {
System.out.println("动物静态方法");
}
}
class Cat extends Animal {
String type = "猫";
public static void test() {
System.out.println("猫静态方法");
}
}
// 测试
Animal animal = new Cat();
System.out.println(animal.type); // 输出"动物"(看引用类型)
animal.test(); // 输出"动物静态方法"(静态方法无多态)