Java多态是运行时动态绑定,根据实际对象类型调用子类重写的方法;字段访问不具多态性,static方法无多态,设计时需明确抽象层级与重写契约。

Java多态不是语法糖,而是运行时根据实际对象类型决定调用哪个方法——animal.shout() 看起来调的是父类方法,实际执行的是子类重写的版本。
为什么 Animal animal = new Dog(); 调用的是 Dog.shout()
这是动态绑定(dynamic binding)在起作用:编译期只检查 Animal 类型是否有 shout() 方法(有,所以通过),运行期才查 animal 实际指向的是 Dog 实例,于是跳转到 Dog.shout()。
关键前提:
-
shout()在Animal中必须是public或protected,且不能是static、final或private -
Dog中的shout()必须用@Override显式标注(非强制但强烈建议,避免签名写错却没覆盖) - 如果去掉
@Override且方法签名稍有不同(比如参数类型写成String而不是Object),就变成重载而非重写,多态失效
shout() 方法里访问成员变量会发生什么
变量访问不走多态,只看引用声明类型。即使 animal 指向 Dog,animal.name 读取的仍是 Animal 类里的 name 字段(如果子类也定义了同名字段,会遮蔽父类字段,但不会自动继承或共享)。
立即学习“Java免费学习笔记(深入)”;
所以别在多态场景下依赖子类字段值参与逻辑——要么把字段设为 protected 并在子类构造器中初始化父类字段,要么改用 getter 方法(方法才有多态):
class Animal {
protected String name = "动物";
public void shout() { System.out.println(name + "叫"); }
}
class Dog extends Animal {
public Dog() { this.name = "狗"; } // 修改父类字段
}
加个 Cat 和 Bird 后,如何统一处理叫声逻辑
用集合存多态对象最常见,但要注意:不能直接对 List 里的元素调用子类特有方法(如 bird.fly()),否则编译报错。需要先判断类型再转型:
- 用
instanceof安全判断:if (a instanceof Bird) { ((Bird) a).fly(); } - Java 14+ 可用模式匹配简化:
if (a instanceof Bird bird) { bird.fly(); } - 更推荐提取共性行为进接口(如
Flyable),让Bird同时实现Animal和Flyable,避免强转
另外,toString()、equals() 这些默认从 Object 继承的方法,只要子类重写,同样参与多态——这点常被忽略。
为什么 static 方法看起来“被重写”其实是假象
static 方法属于类,不是对象,调用时只看引用类型,不看实际对象。下面代码输出两行 “动物叫”,而不是 “狗叫”:
class Animal { static void shout() { System.out.println("动物叫"); } }
class Dog extends Animal { static void shout() { System.out.println("狗叫"); } }
Animal a = new Dog();
a.shout(); // 输出"动物叫"
Dog.shout(); // 输出"狗叫"
这根本不是多态,只是编译器根据 a 的声明类型(Animal)静态绑定了方法。真要实现类似效果,得靠实例方法 + 多态。
多态真正的复杂点不在写法,而在设计意图是否清晰:父类方法该不该有默认实现?子类是否必须重写?要不要抽象成 abstract?这些决定了调用方能否安全地只面向 Animal 编程,而不用知道背后是狗还是猫。










