Java 多态
多态(Polymorphism)是面向对象第三大特性,也是最强大的一个——它让"父类引用调用子类方法"成为可能,是设计模式、框架扩展、开闭原则的底层支撑。本章带你彻底搞懂多态。
1. 多态的三大要素
多态出现需要三个条件:
- 继承:子类继承父类(或实现接口)。
- 重写:子类重写父类方法。
- 父类引用指向子类对象:
Animal a = new Dog();
满足这三个,就出现了多态:
class Animal {
public void speak() {
System.out.println("动物发出声音");
}
}
class Dog extends Animal {
@Override
public void speak() {
System.out.println("汪汪!");
}
}
class Cat extends Animal {
@Override
public void speak() {
System.out.println("喵喵!");
}
}
public class PolymorphismDemo {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Animal a1 = new Dog(); // 向上转型(隐式)
Animal a2 = new Cat();
// 同一个调用,不同行为 —— 这就是多态!
a1.speak(); // 汪汪!
a2.speak(); // 喵喵!
}
}核心思想:同一个方法调用,根据对象的实际类型,执行不同的实现。编译时只看到父类,运行时动态绑定到子类。
2. 向上转型(upcasting)
把子类对象赋值给父类引用,叫向上转型——安全、自动:
class Animal {
String name = "Animal 类的 name";
public void eat() { System.out.println("Animal 吃东西"); }
public static void staticMethod() { System.out.println("Animal static"); }
}
class Dog extends Animal {
String name = "Dog 类的 name";
@Override
public void eat() { System.out.println("Dog 啃骨头"); }
public void bark() { System.out.println("Dog 汪汪"); }
public static void staticMethod() { System.out.println("Dog static"); }
}
public class UpcastDemo {
public static void main(String[] args) {
Animal a = new Dog(); // 向上转型
// ✅ 通过父类引用能调用的:父类有的方法(运行时按子类)
a.eat(); // Dog 啃骨头(动态绑定)
// ❌ 通过父类引用不能调用子类特有方法
// a.bark(); // 编译报错:Animal 没有 bark
// ⚠️ 字段不参与多态!编译看父类,运行也看父类
System.out.println(a.name); // Animal 类的 name
// ⚠️ 静态方法不参与多态!编译看父类,运行也看父类
a.staticMethod(); // Animal static
// ⚠️ 静态方法应该用类名调用
Dog.staticMethod(); // Dog static
}
}向上转型的限制(新手要牢记):
- 只能调用父类有的方法(即使是子类特有的,也调不到)。
- 字段不参与多态(编译看父类,运行也看父类)。
- 静态方法不参与多态(静态属于类,不存在"重写")。
- 实例方法才参与多态——运行时动态绑定到真实对象的方法。
3. 动态绑定:多态的底层原理
"动态绑定"指 JVM 在运行时根据对象的实际类型决定调用哪个方法。来看多态的真正威力——同一份代码处理多种类型:
class Shape {
public double area() { return 0; }
public String describe() {
// 这里调用的 area() 会被动态绑定 —— 运行时根据真实对象决定
return "面积 = " + area();
}
}
class Circle extends Shape {
private double r;
public Circle(double r) { this.r = r; }
@Override public double area() { return Math.PI * r * r; }
}
class Rectangle extends Shape {
private double w, h;
public Rectangle(double w, double h) { this.w = w; this.h = h; }
@Override public double area() { return w * h; }
}
public class DynamicBinding {
// 多态的真正威力:同一份代码处理多种类型
public static void printArea(Shape s) {
// s 可能是 Circle、Rectangle 或其他子类
// 编译时不知道实际类型,运行时自动绑定正确的方法
System.out.println(s.describe());
}
public static void main(String[] args) {
printArea(new Circle(2)); // 面积 = 12.566...
printArea(new Rectangle(3, 4)); // 面积 = 12.0
// 把不同子类放进父类数组,统一处理
Shape[] shapes = { new Circle(1), new Rectangle(2, 3), new Circle(5) };
double total = 0;
for (Shape s : shapes) {
total += s.area(); // 动态绑定到正确的方法
}
System.out.println("总面积: " + total);
}
}这就是多态最大的价值:printArea(Shape s) 方法不需要知道传入的是 Circle 还是 Rectangle,运行时自动调用正确版本。新增一个 Triangle 子类,printArea 一行不用改——这就是"对扩展开放,对修改关闭"(开闭原则)。
4. 多态的典型应用场景
- 方法参数:
void process(Shape s)可以接收任何 Shape 子类,提高方法通用性。 - 集合存储:
List<Animal> zoo可以放 Dog、Cat、Bird,统一遍历。 - 返回值:
Shape createShape()可以返回任何 Shape 子类。 - 框架扩展:Spring 的
ApplicationContext、Servlet 的HttpServlet都基于多态做扩展。 - 设计模式:策略模式、模板方法、状态模式全靠多态实现。
5. instanceof 与向下转型
向上转型后,父类引用无法直接调用子类特有方法。如果你确实需要调,要先向下转型(强转回子类)。但强转不安全——转错类型会抛 ClassCastException。正确做法:先用 instanceof 判断,再强转。
class Animal {
public void eat() { System.out.println("吃"); }
}
class Dog extends Animal {
public void bark() { System.out.println("汪汪"); }
}
class Cat extends Animal {
public void meow() { System.out.println("喵喵"); }
}
public class InstanceofDemo {
public static void main(String[] args) {
Animal a = new Dog(); // 向上转型
// 1. instanceof:判断对象的真实类型
System.out.println(a instanceof Animal); // true
System.out.println(a instanceof Dog); // true
System.out.println(a instanceof Cat); // false
// 2. 向下转型:把父类引用强转回子类
if (a instanceof Dog) {
Dog d = (Dog) a; // 安全向下转型
d.bark(); // 汪汪(子类特有方法)
}
// 3. Java 16+ 模式匹配(更简洁)
if (a instanceof Dog d) {
d.bark(); // 直接用 d
}
// ⚠️ 强转错误类型会抛 ClassCastException
// Cat c = (Cat) a; // ❌ 运行时报错(Dog 不能转 Cat)
// 安全做法:先 instanceof 判断,再强转
if (a instanceof Cat) {
((Cat) a).meow();
} else {
System.out.println("不是猫");
}
}
}经验法则:
- 能用多态就不用向下转型——多态是更优雅的方案。
- 必须向下转型时,先 instanceof 再转(避免 ClassCastException)。
- Java 16+ 的模式匹配写法更简洁,优先使用。
- 如果代码里出现大量
instanceof + 强转,说明设计可能有问题——考虑用多态重构。
6. 多态的"陷阱"
新手最常踩的坑:
- 误以为字段也多态:字段按编译时类型决定,无多态。
- 误以为 static 方法也多态:static 属于类,不存在重写,无多态。
- private 方法不参与多态:子类"重写" private 方法其实是新方法(父类看不见)。
- 构造方法内调用可重写方法:可能导致子类还没初始化就被调用,出 bug。构造方法里别调可重写方法。
小结
这一章你掌握了 Java 多态的全部要点:三大要素、向上转型、动态绑定、instanceof、向下转型。多态是 Java 面向对象的精髓——理解了它,你才看得懂 Spring、Servlet、所有 Java 框架的扩展机制。下一篇我们看 抽象类与接口——多态的"基石"。
← 上一篇 Java 继承
下一篇 Java 抽象类与接口 →