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判断,确保类型安全。
-
java
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("动物在吃东西");
}
}
java
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("猫抓老鼠");
}
}
java
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("狗在看家");
}
}
java
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("没有这种动物");
}
}
}
java
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,"🐟");
}
}