返回首页

java多态

1430 字 7 分钟
多态#

在 Java 里,**多态(Polymorphism)**是面向对象三个核心特性之一:封装、继承、多态。 你现在可以先把多态记成一句话:

同一个父类类型,可以指向不同的子类对象;调用同一个方法时,会根据实际对象执行不同的行为。

1. 先看一个最简单的例子#

假设有一个父类 Animal

class Animal {
public void speak() {
System.out.println("动物发出声音");
}
}

两个子类分别重写 speak()

class Dog extends Animal {
@Override
public void speak() {
System.out.println("狗:汪汪汪");
}
}
class Cat extends Animal {
@Override
public void speak() {
System.out.println("猫:喵喵喵");
}
}

然后:

Animal a1 = new Dog();
Animal a2 = new Cat();
a1.speak();
a2.speak();

输出:

狗:汪汪汪
猫:喵喵喵

关键就在这里:

Animal a1 = new Dog();
Animal a2 = new Cat();

变量的类型都是:

Animal

但实际创建的对象分别是:

Dog
Cat

所以执行:

a1.speak();

Java 会发现 a1 实际指向的是 Dog 对象,于是调用:

Dog.speak()

这就是多态。


2. 多态最重要的理解:左边看类型,右边看对象#

例如:

Animal animal = new Dog();

可以把它拆成:

Animal animal
引用类型 / 编译时类型
new Dog()
实际对象 / 运行时类型

所以:

animal.speak();

如果 Dog 重写了 speak(),最终执行的是:

Dog 的 speak()

这叫做动态绑定 / 动态方法分派

也就是说:

编译的时候看左边,运行的时候调用哪个被重写的方法主要看右边实际是什么对象。

这句话在学习 Java 多态时非常重要。


3. 为什么需要多态?#

如果没有多态,假设你要让很多动物发出声音:

Dog dog = new Dog();
Cat cat = new Cat();
dog.speak();
cat.speak();

动物越来越多:

Dog dog = new Dog();
Cat cat = new Cat();
Tiger tiger = new Tiger();
Bird bird = new Bird();

代码会越来越依赖具体类型。

有多态之后,可以统一写:

Animal dog = new Dog();
Animal cat = new Cat();
Animal tiger = new Tiger();
dog.speak();
cat.speak();
tiger.speak();

甚至可以:

public static void makeSound(Animal animal) {
animal.speak();
}

然后:

makeSound(new Dog());
makeSound(new Cat());
makeSound(new Tiger());

makeSound() 根本不用关心传进来的是狗、猫还是老虎。

只需要知道:

它是一个 Animal

然后:

animal.speak();

不同对象自己决定应该怎么叫。

这才是多态真正有价值的地方:让程序依赖抽象,而不是依赖具体实现。


4. 一个更接近实际开发的例子#

以后做 Java 项目,你会经常看到这种代码:

interface Payment {
void pay(double money);
}

支付宝:

class Alipay implements Payment {
@Override
public void pay(double money) {
System.out.println("支付宝支付:" + money);
}
}

微信支付:

class WeChatPay implements Payment {
@Override
public void pay(double money) {
System.out.println("微信支付:" + money);
}
}

银行卡:

class BankCardPay implements Payment {
@Override
public void pay(double money) {
System.out.println("银行卡支付:" + money);
}
}

业务代码可以只写:

public void checkout(Payment payment) {
payment.pay(100);
}

于是:

checkout(new Alipay());
checkout(new WeChatPay());
checkout(new BankCardPay());

checkout() 不需要针对支付宝写一个版本、微信写一个版本、银行卡再写一个版本。

这就是实际开发中多态非常重要的原因。


5. 多态通常需要满足什么条件?#

你目前学习阶段可以记住三个条件:

  1. 存在继承或接口实现关系

  2. 子类重写父类方法

  3. 父类/接口引用指向子类对象

经典形式:

父类类型 变量 = new 子类();

例如:

Animal animal = new Dog();

或者接口:

Payment payment = new Alipay();

然后:

payment.pay(100);

执行的是 Alipay 重写后的 pay()


6. 一个特别容易考的坑#

看:

class Animal {
public void eat() {
System.out.println("eat");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("汪汪");
}
}

然后:

Animal animal = new Dog();

下面这个可以:

animal.eat();

但这个不可以:

animal.bark(); // 编译错误

你可能会觉得:

animal 明明指向的是 Dog,为什么不能调用 bark()?

因为变量能够调用哪些方法,首先看引用类型

这里:

Animal animal

编译器看到的是 Animal 类型。

Animal 里面没有:

bark()

所以编译直接不通过。

但是对于父类本来就存在、又被子类重写的方法:

animal.speak();

编译器看到:

Animal 有 speak()

所以允许调用。

运行的时候再发现:

实际对象是 Dog

于是执行 Dog.speak()

所以可以总结成一个非常实用的口诀:

能不能调用,看左边;实际执行谁,看右边。

严格来说这个口诀主要针对实例方法的重写和多态调用,字段、静态方法等情况不能简单套用。


7. 再看一道面试/考试常见题#
class Animal {
public void speak() {
System.out.println("Animal");
}
}
class Dog extends Animal {
@Override
public void speak() {
System.out.println("Dog");
}
public void run() {
System.out.println("Dog running");
}
}

执行:

Animal animal = new Dog();
animal.speak();

答案:

Dog

因为 Dog 重写了 speak()

但是:

animal.run();

会:

编译错误

因为引用类型 Animal 中不存在 run()

如果真的需要调用:

Dog dog = (Dog) animal;
dog.run();

这就是后面会学习的向下转型


8. 把继承、重写、多态串起来#

这三个知识点其实是一条链:

继承
Animal ← Dog
重写 speak()
Animal a = new Dog();
多态
a.speak()
实际执行 Dog.speak()

所以学习顺序最好是:

继承 → 方法重写 → 向上转型 → 多态 → 向下转型 → instanceof → 抽象类 → 接口

你只要真正理解下面这段代码,多态的核心基本就掌握了:

Animal animal = new Dog();
animal.speak();

脑子里同时想到:

左边 Animal
→ 决定我“能调用什么”
右边 Dog
→ 决定被重写的方法“实际执行谁”

父类引用可以调用父类声明的方法;如果实际子类对象重写了该方法,就执行子类版本,否则执行继承的父类版本。

这就是 Java 多态最核心的思想。

java多态
https://335264.xyz/posts/java多态/
作者
刺儿菜
发布于
2026-09-10
许可协议
CC BY-NC-SA 4.0
Comments

评论