Java多态:三大条件与实现详解

一、什么是多态

多态是指:同一操作(方法调用)作用于不同类型的对象上,会产生不同的执行结果。

简单来说:父类(或接口)的引用可以指向子类(或实现类)的实例,调用该引用的方法时,会根据对象的实际类型(而非引用类型)执行对应的方法实现,实现 "一种接口,多种实现"。

多态的底层支撑是动态绑定(后期绑定),编译时无法确定具体执行的方法,运行时才会完成方法的匹配与调用。

二、实现多态的三大必要条件

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(); // 输出"动物静态方法"(静态方法无多态)
相关推荐
老蒋每日coding2 小时前
Java解析Excel并对特定内容做解析成功与否的颜色标记
java·开发语言·excel
lang201509282 小时前
Java反射利器:Apache Commons BeanUtils详解
java·开发语言·apache
m0_748245922 小时前
SQLite 数据类型概述
java·数据库·sqlite
沐知全栈开发2 小时前
HTML DOM 方法
开发语言
扶苏10022 小时前
前端js高频面试点汇总
开发语言·前端·javascript
项目題供诗2 小时前
C语言基础(五)
c语言·开发语言
Mh_ithrha2 小时前
题目:小鱼比可爱(java)
java·开发语言·算法
l1t2 小时前
数独优化求解C库tdoku-lib的使用
c语言·开发语言·python·算法·数独
wxm6312 小时前
力扣算法题(C++):1、2
java·算法·leetcode