Java 多态 (Polymorphism)
多态是面向对象三大特性中最核心、最灵活的特性。它意味着“同一行为,多种实现”。
一、多态的基本概念
定义:同一种类型的对象(或引用),在不同的情况下表现出不同的行为和状态。
核心表现形式(向上转型):
java
父类类型 引用名 = new 子类对象();
例如:
java
Animal a = new Dog(); // 多态 Animal b = new Cat(); // 多态
这里的
Animal是编译时类型,而Dog或Cat是运行时类型。多态的前提条件(三者缺一不可):
有继承关系或实现关系(接口)。
有父类(或接口)引用指向子类对象(即向上转型)。
有方法的重写(否则多态调用方法没有实际意义)。
二、多态的优势
降低耦合度,提高扩展性:方法参数或容器(如集合)使用父类类型,可以接受任何子类对象,代码通用性更强。
便于维护:当需要新增一种子类时,无需修改使用父类引用的代码。
代码简洁:统一的接口,不同的实现。
示例:体现扩展性的优势
java
class Animal { public void eat() { System.out.println("动物吃东西"); } } class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); } } class Cat extends Animal { @Override public void eat() { System.out.println("猫吃鱼"); } } // 饲养员类 - 核心优势体现 class Keeper { // 参数使用父类类型,可以接收任何Animal子类对象 public void feedAnimal(Animal a) { a.eat(); // 运行时执行具体子类的eat方法 } } public class Main { public static void main(String[] args) { Keeper keeper = new Keeper(); Animal dog = new Dog(); Animal cat = new Cat(); keeper.feedAnimal(dog); // 输出:狗吃骨头 keeper.feedAnimal(cat); // 输出:猫吃鱼 // 未来新增一个Tiger类,Keeper类完全无需修改! // keeper.feedAnimal(new Tiger()); } }三、多态调用成员的特点(记忆口诀)
这是理解多态行为的关键:
| 成员类型 | 编译阶段(看左边) | 运行阶段(看右边) | 说明 |
|---|---|---|---|
| 成员变量 | 检查父类中是否有该变量 | 访问父类中的该变量 | 编译和运行都看左边(父类) |
| 成员方法 | 检查父类中是否有该方法声明 | 执行子类中重写后的该方法 | 编译看左边,运行看右边(子类) |
示例:
java
class Fu { int num = 10; public void show() { System.out.println("Fu show"); } } class Zi extends Fu { int num = 20; @Override public void show() { System.out.println("Zi show"); } public void special() { System.out.println("Zi special"); } } public class Test { public static void main(String[] args) { Fu obj = new Zi(); // 多态 // 访问成员变量:看左边(Fu) System.out.println(obj.num); // 输出:10 // 调用成员方法:编译看左边(Fu),运行看右边(Zi) obj.show(); // 输出:Zi show // obj.special(); // 编译错误!因为编译看左边,Fu类中没有special方法声明 } }四、多态的弊端与解决方案:类型转换
弊端:在多态的形式下(父类引用),不能调用子类特有的方法或访问子类特有的变量。
解决方案:使用引用数据类型转换。
1. 向上转型 (Upcasting)
自动进行:子类类型转换为父类类型。
Animal a = new Dog();// 安全,因为狗一定是动物。
2. 向下转型 (Downcasting)
强制进行:父类类型转换为子类类型。
语法:
子类类型 引用名 = (子类类型) 父类引用;目的:为了“还原”对象的真实子类类型,以便调用子类特有的功能。
示例:
java
Animal a = new Dog(); // 向上转型 Dog d = (Dog) a; // 向下转型,将a强制转换为Dog类型 d.lookHome(); // 现在可以调用Dog特有的方法了
3. 转型的风险与安全校验
如果转型的目标类型与对象的真实类型不匹配,会抛出ClassCastException(类型转换异常)。
java
Animal a = new Cat(); Dog d = (Dog) a; // 运行时错误!ClassCastException: Cat cannot be cast to Dog
安全转型:使用instanceof关键字
作用:在转型前进行判断,避免
ClassCastException。语法:
引用变量 instanceof 数据类型结果:返回
boolean值。判断引用指向的对象是否属于该类型(或该类型的子类)。
示例:
java
public static void checkAnimal(Animal a) { a.eat(); // 所有动物都有的行为 // 类型判断与安全转型 if (a instanceof Dog) { Dog dog = (Dog) a; dog.lookHome(); } else if (a instanceof Cat) { Cat cat = (Cat) a; cat.catchMouse(); } else { System.out.println("这是其他动物"); } }五、总结与最佳实践
多态的本质:通过父类/接口引用调用方法,执行时由JVM根据对象的实际类型决定调用哪个版本的方法(动态绑定)。
使用场景:当需要编写通用代码、处理一组相关对象、或设计可扩展的系统架构时。
转型原则:
能向上转型就向上转型(利用多态,提高代码通用性)。
必须向下转型时才转(需要使用子类特有功能时)。
向下转型前务必用
instanceof判断,确保类型安全。
package polymorphism02; public class Animal { private int age; private String color; public Animal() { } public Animal(int age, String color) { this.age = age; this.color = color; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getColor() { return color; } public void setColor(String color) { this.color = color; } public void eat(String something){ System.out.println("动物在吃东西"); } }package polymorphism02; public class Cat extends Animal{ public Cat() { } public Cat(int age, String color) { super(age, color); } @Override public void eat(String something){ System.out.println(getAge()+"岁的"+getColor()+"颜色的猫在吃"+something); } public void catchMouse(){ System.out.println("猫抓老鼠"); } }package polymorphism02; public class Dog extends Animal{ public Dog() { } public Dog(int age, String color) { super(age, color); } @Override public void eat(String something){ System.out.println(getAge()+"岁的"+getColor()+"颜色的狗在吃"+something); } public void lookHome(){ System.out.println("狗在看家"); } }package polymorphism02; public class Person { private String name; private int age; public Person() { } public Person(int age, String name) { this.age = age; this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } //饲养狗 /*public void keepPet(Dog dog,String something){ System.out.println("年龄为"+age+"岁的"+name+"养了一只"+dog.getColor()+"颜色的"+dog.getAge()+"岁的狗"); dog.eat(something); } //饲养猫 public void keepPet(Cat cat,String something){ System.out.println("年龄为"+age+"岁的"+name+"养了一只"+cat.getColor()+"颜色的"+cat.getAge()+"岁的猫"); cat.eat(something); }*/ //一个方法能接收使用动物信息 //方法的形参:可以写这些类的父类 public void keepPet(Animal a ,String something){ if (a instanceof Dog d){ System.out.println("年龄为"+age+"岁的"+name+"养了一只"+d.getColor()+"颜色的"+d.getAge()+"岁的狗"); d.eat(something); }else if (a instanceof Cat c){ System.out.println("年龄为"+age+"岁的"+name+"养了一只"+c.getColor()+"颜色的"+c.getAge()+"岁的猫"); c.eat(something); }else { System.out.println("没有这种动物"); } } }package polymorphism02; public class Test { public static void main(String[] args) { //创建对象并调用 /*Person p = new Person(30, "老王"); Dog d = new Dog(2,"黑"); p.keepPet(d,"骨头"); Person p1 = new Person(26, "老李"); Cat d1 = new Cat(3,"黄"); p1.keepPet(d1,"🐟"); */ Person p = new Person(30, "老王"); Dog d = new Dog(2,"黑"); Cat c = new Cat(3,"黄"); p.keepPet(d,"骨头"); p.keepPet(c,"🐟"); } }