目录写在前面一、多态的概念用生活场景理解1.1 什么是多态1.2 多态在Java中的定义二、多态的实现条件2.1 继承2.2 重写2.3 父类引用指向子类对象三、向上转型与向下转型3.1 向上转型Upcasting3.2 向下转型Downcasting3.3 instanceof安全转型的判断工具四、重写Override详解4.1 什么是重写4.2 重写的规则4.3 重写 vs 重载4.4 重写的注意事项五、理解多态的底层原理5.1 静态绑定 vs 动态绑定5.2 动态绑定的过程六、多态的优势6.1 降低耦合度6.2 提高代码的可扩展性6.3 让代码更简洁七、多态的实际应用场景7.1 方法参数多态7.2 返回值多态7.3 数组多态八、思考题检验你的理解题目1判断输出结果题目2判断输出结果题目3以下哪些是多态的必要条件总结写在前面在前一篇文章中我们学习了Java的继承机制。通过继承子类可以复用父类的代码并在其基础上扩展自己的功能。但继承的真正威力需要结合多态才能完全发挥出来。多态是面向对象编程的三大特性之一封装、继承、多态也是Java中最具魅力的特性之一。它让同一个行为在不同的对象身上表现出不同的形态极大地提升了代码的灵活性和可扩展性。今天这篇文章我们就来深入理解Java中的多态机制。一、多态的概念用生活场景理解1.1 什么是多态多态这个词的字面意思是多种形态。在编程中多态指的是同一个消息不同的对象接收到后会做出不同的响应。用一个生活中的例子来理解你对着狗喊一声过来狗会摇着尾巴跑向你。你对着猫喊一声过来猫可能会看你一眼然后优雅地走开。你对着鸟喊一声过来鸟可能会飞到你肩膀上。同样是过来这个指令不同的动物做出了不同的反应。这就是多态——同一个行为不同的表现形式。1.2 多态在Java中的定义在Java中多态是指父类的引用指向子类的对象调用同一个方法时表现出不同的行为。Animal animal1 new Dog(); // 父类引用指向子类对象 Animal animal2 new Cat(); // 父类引用指向子类对象 animal1.shout(); // 狗叫汪汪汪 animal2.shout(); // 猫叫喵喵喵同样是shout()方法因为实际的对象不同表现出来的行为也不同。二、多态的实现条件在Java中要实现多态必须满足以下三个条件2.1 继承必须有父类和子类之间的继承关系。子类继承父类才能拥有父类的行为。2.2 重写子类必须重写父类的方法。如果子类没有重写调用的是父类的方法就不会有多态的效果。2.3 父类引用指向子类对象也就是我们常说的向上转型用父类的引用来接收子类的对象。// 满足三个条件的完整示例 class Animal { public void shout() { System.out.println(动物发出叫声); } } class Dog extends Animal { // 条件1继承 Override public void shout() { // 条件2重写 System.out.println(汪汪汪); } } public class Test { public static void main(String[] args) { Animal animal new Dog(); // 条件3父类引用指向子类对象 animal.shout(); // 汪汪汪多态 } }三、向上转型与向下转型3.1 向上转型Upcasting向上转型是指子类对象赋值给父类引用。// 语法格式 父类类型 引用名 new 子类类型(); // 示例 Animal animal new Dog();向上转型是安全的因为子类一定包含了父类的所有成员。这就像说狗是一种动物逻辑上是成立的。向上转型后的访问限制可以访问父类中定义的方法和变量不能访问子类特有的方法和变量调用重写的方法时执行的是子类的版本多态的核心class Animal { public void eat() { System.out.println(动物在吃东西); } } class Dog extends Animal { Override public void eat() { System.out.println(狗在啃骨头); } public void bark() { System.out.println(汪汪汪); } } public class Test { public static void main(String[] args) { Animal animal new Dog(); animal.eat(); // 狗在啃骨头多态 // animal.bark(); // 编译错误父类引用不能访问子类特有方法 } }3.2 向下转型Downcasting向下转型是指父类引用强制转换回子类引用。// 语法格式 子类类型 引用名 (子类类型) 父类引用; // 示例 Animal animal new Dog(); Dog dog (Dog) animal; // 向下转型 dog.bark(); // 现在可以访问子类特有方法了向下转型是不安全的需要谨慎使用。如果父类引用实际指向的不是该子类类型运行时会抛出ClassCastException。Animal animal new Cat(); Dog dog (Dog) animal; // 运行时异常Cat不能转成Dog3.3 instanceof安全转型的判断工具为了避免向下转型时出现类型转换异常Java提供了instanceof关键字来判断对象的真实类型Animal animal new Dog(); if (animal instanceof Dog) { Dog dog (Dog) animal; dog.bark(); } else if (animal instanceof Cat) { Cat cat (Cat) animal; cat.mew(); }使用规则对象 instanceof 类判断对象是否是某个类的实例如果是返回true否则返回false如果对象为null返回false最佳实践在进行向下转型之前一定要先用instanceof进行检查。四、重写Override详解4.1 什么是重写重写Override是指子类对父类中允许访问的方法的实现过程进行重新编写。重写是实现多态的基础。4.2 重写的规则class Animal { public void eat() { System.out.println(动物在吃东西); } } class Dog extends Animal { Override // 推荐加上这个注解让编译器帮我们检查重写是否正确 public void eat() { System.out.println(狗在啃骨头); } }重写的核心规则方法名必须相同参数列表必须相同返回值类型必须相同或者是父类返回值类型的子类称为协变返回类型访问权限不能比父类更严格父类是public子类不能是private声明抛出的异常不能比父类更宽泛被static、final、private修饰的方法不能被重写4.3 重写 vs 重载这两个概念容易混淆我们来做一个清晰的对比对比维度重写Override重载Overload发生位置子类和父类之间同一个类中方法名必须相同必须相同参数列表必须相同必须不同返回值必须相同或子类可以不同访问权限不能更严格没有限制关键字Override无目的改变父类行为提供多种调用方式一句话区分重写是父子类之间的关系重载是同一个类中的多种形式。4.4 重写的注意事项静态方法不能被重写class Parent { public static void method() { System.out.println(父类静态方法); } } class Child extends Parent { // 这不是重写这是重新定义了一个静态方法 public static void method() { System.out.println(子类静态方法); } } // 调用时看引用类型不看实际对象 Parent p new Child(); p.method(); // 父类静态方法静态绑定private方法不能被重写private方法在子类中不可见所以不存在重写的概念。final方法不能被重写final修饰的方法是为了防止被子类修改。五、理解多态的底层原理5.1 静态绑定 vs 动态绑定在Java中方法的调用分为两种绑定方式静态绑定前期绑定在编译阶段就确定了要调用哪个方法适用于static方法、final方法、private方法、构造方法效率较高动态绑定后期绑定在运行阶段根据对象的实际类型来确定调用哪个方法适用于普通成员方法非static、final、private是多态实现的底层机制class Animal { public void eat() { // 动态绑定 System.out.println(动物吃东西); } public static void sleep() { // 静态绑定 System.out.println(动物睡觉); } } class Dog extends Animal { Override public void eat() { System.out.println(狗啃骨头); } } public class Test { public static void main(String[] args) { Animal a new Dog(); a.eat(); // 动态绑定 → 狗啃骨头 a.sleep(); // 静态绑定 → 动物睡觉 } }5.2 动态绑定的过程当JVM调用一个对象的方法时动态绑定遵循以下步骤从对象的实际类型开始查找该方法如果找到了就调用它如果没找到沿着继承链向上查找父类直到找到Object类为止如果还没找到编译报错这就是为什么Animal a new Dog(); a.eat()会调用Dog类的eat()方法——因为实际对象的类型是Dog。六、多态的优势6.1 降低耦合度不使用多态时如果我们要让不同的动物叫需要写很多if-elsepublic void makeSound(Animal animal) { if (animal instanceof Dog) { ((Dog) animal).bark(); } else if (animal instanceof Cat) { ((Cat) animal).mew(); } else if (animal instanceof Bird) { ((Bird) animal).chirp(); } }每增加一种动物就要修改这个方法。这违反了开闭原则对扩展开放对修改关闭。使用多态后public void makeSound(Animal animal) { animal.shout(); // 不管什么动物调用同一个方法 }增加新动物时只需要让新动物继承Animal并重写shout方法即可不需要修改makeSound方法。6.2 提高代码的可扩展性// 定义一个方法参数是父类类型 public void feed(Animal animal) { animal.eat(); } // 可以传入任何子类对象 feed(new Dog()); // 狗在吃东西 feed(new Cat()); // 猫在吃东西 feed(new Bird()); // 鸟在吃东西如果以后增加了Fish类feed方法不需要做任何修改直接传入Fish对象即可。6.3 让代码更简洁// 不使用多态需要为每种动物单独写方法 public void feedDog(Dog dog) { ... } public void feedCat(Cat cat) { ... } public void feedBird(Bird bird) { ... } // 使用多态一个方法搞定所有 public void feed(Animal animal) { ... }七、多态的实际应用场景7.1 方法参数多态最常见的使用方式方法参数声明为父类类型实际传入子类对象。public class ZooKeeper { public void feed(Animal animal) { animal.eat(); } } // 使用 ZooKeeper keeper new ZooKeeper(); keeper.feed(new Dog()); keeper.feed(new Cat());7.2 返回值多态方法的返回值声明为父类类型实际返回子类对象。public Animal getPet(int type) { if (type 1) { return new Dog(); } else if (type 2) { return new Cat(); } return null; }7.3 数组多态创建父类类型的数组存储不同类型的子类对象。Animal[] animals new Animal[3]; animals[0] new Dog(); animals[1] new Cat(); animals[2] new Bird(); for (Animal animal : animals) { animal.shout(); // 多态每个动物用自己的方式叫 }八、思考题检验你的理解题目1判断输出结果class A { public void show() { System.out.println(A); } } class B extends A { Override public void show() { System.out.println(B); } } public class Test { public static void main(String[] args) { A obj new B(); obj.show(); } }答案输出B。虽然引用类型是A但实际对象是B动态绑定调用B的show方法。题目2判断输出结果class A { public static void test() { System.out.println(A); } } class B extends A { public static void test() { System.out.println(B); } } public class Test { public static void main(String[] args) { A obj new B(); obj.test(); } }答案输出A。静态方法属于类采用静态绑定调用时取决于引用类型A而不是实际对象类型B。题目3以下哪些是多态的必要条件A. 继承B. 重载C. 重写D. 父类引用指向子类对象答案A、C、D。重载与多态无关多态需要继承、重写和向上转型三个条件。总结今天我们学习了Java多态的完整知识体系多态的概念同一个行为不同的表现形式实现条件继承 重写 父类引用指向子类对象向上转型子类对象赋值给父类引用安全但不能访问子类特有成员向下转型父类引用转回子类引用不安全需用instanceof检查重写规则方法签名必须相同访问权限不能更严格底层原理动态绑定运行时根据实际类型确定调用哪个方法多态优势降低耦合、提高扩展性、让代码更简洁多态是面向对象编程的精髓之一它将继承的力量发挥到了极致。掌握了多态你的代码将从写死逻辑升级为设计架构。下一篇文章我们将学习Java中的抽象类和接口敬请期待