多态的核心是“编译看左边,运行看右边”:左边声明类型决定可见成员,右边实际类型决定执行方法;成员变量不参与多态,仅方法调用体现运行时绑定;重载属编译时多态,重写才是真正的运行时多态。

多态性不是靠背概念记住的,而是通过“看谁调用、谁实际干活”这个核心逻辑一点点建立直觉的。关键不在记术语,而在看清编译和运行两个阶段到底发生了什么。
先搞清最核心的一句话:编译看左边,运行看右边
这句话是理解多态的钥匙,不是口诀,是事实:
-
左边指声明类型(比如
Animal a = ...中的Animal),它决定了你能“看到”哪些方法和变量——只能调用Animal类里定义过的东西; -
右边指实际创建的对象类型(比如
new Dog()),它决定了“真正执行”的是哪个版本的方法——只要该方法被子类重写(@Override),就执行子类的逻辑。 - 注意:成员变量不参与多态。哪怕子类有同名字段,
a.name永远取的是Animal类里的值;只有方法调用才体现“运行看右边”。
动手验证三步法:写、改、测
别只读例子,自己敲三行代码就能建立肌肉记忆:
- 写一个父类(如
Shape)带draw()方法; - 写两个子类(
Circle、Square)都重写draw(),打印不同字符串; - 写测试代码:
Shape s1 = new Circle(); s1.draw();和Shape s2 = new Square(); s2.draw();——观察输出结果,再把s1改成new Square(),立刻看到行为切换。
向下转型不是“补救”,而是明确需求的信号
当你发现父类引用不能调用子类特有方法(比如 dog.earnMoney() 在 Person p = new Man() 后不可用),这不是多态的缺陷,而是设计意图的提醒:
- 如果业务确实需要访问子类专属能力,说明当前抽象层级不够——要么把方法提到父类(或接口),要么做安全的向下转型;
- 转型前务必用
instanceof判断,避免ClassCastException; - 频繁转型往往意味着接口设计可以优化,比如提取共用行为到接口中。
区分两种“多态”,别混在一起学
初学者容易把“重载”和“重写”都叫多态,但它们发生时机完全不同:
- 编译时多态(重载):方法名相同、参数不同,编译器在编译阶段就决定调用哪个——跟对象类型无关,也不需要继承;
- 运行时多态(重写):子类覆盖父类方法,调用哪个版本由运行时对象真实类型决定——必须有继承/实现关系,且依赖动态绑定。
- 真正体现面向对象灵活性的,是后者。前者只是语法糖,前者练熟了对理解后者反而可能造成干扰。










