Java 多态

多态(Polymorphism)是面向对象第三大特性,也是最强大的一个——它让"父类引用调用子类方法"成为可能,是设计模式、框架扩展、开闭原则的底层支撑。本章带你彻底搞懂多态。

1. 多态的三大要素

多态出现需要三个条件:

满足这三个,就出现了多态:

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. 多态的典型应用场景

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("不是猫");
        }
    }
}

经验法则:

6. 多态的"陷阱"

新手最常踩的坑:

小结

这一章你掌握了 Java 多态的全部要点:三大要素、向上转型、动态绑定、instanceof、向下转型。多态是 Java 面向对象的精髓——理解了它,你才看得懂 Spring、Servlet、所有 Java 框架的扩展机制。下一篇我们看 抽象类与接口——多态的"基石"。

← 上一篇 Java 继承

下一篇 Java 抽象类与接口

✈️💬