父类引用指向子类对象是Java多态唯一生效方式,本质是JVM运行时通过invokevirtual指令查虚方法表动态绑定子类重写方法,实现“编译看父类、运行看子类”,支撑开闭原则、策略模式与依赖倒置。

父类引用指向子类对象,不是语法技巧,而是多态真正起作用的唯一方式。它让代码只依赖“能做什么”,而不是“谁来做的”。比如 Animal a = new Dog(); 这一行,表面是类型声明,背后是解耦、可扩展和可测试的设计起点。
它到底长什么样?
就是日常写出来的普通代码:
-
Animal a = new Dog();—— 抽象类型声明,具体实现创建 -
List<String> list = new ArrayList<>();—— 接口引用 + 具体类实例 -
Runnable task = () -> System.out.println("run");—— 函数式接口也是同一逻辑
关键不在写法多特别,而在于 JVM 在运行时自动查到实际对象类型,并调用其重写的方法——这个过程叫动态绑定。
为什么不能直接用子类变量?
写 Dog d = new Dog(); 没问题,但一加新类型就容易卡住:
- 新增
Cat类,所有用Dog的地方都要改类型、改构造、改逻辑 - 支付模块写死
WechatPay pay = new WechatPay();,换支付宝就得动业务代码 - 单元测试难 mock:想临时替换为模拟实现,必须改方法签名或重构入参
而用 Payment pay = new WechatPay();,后续换成 new Alipay(),甚至通过配置或工厂注入,业务层完全无感。
它怎么支撑真实项目演进?
这不是理论概念,是工程落地的基础设施:
- 开闭原则落地:新增子类(如新动物、新支付渠道)不用改已有调用逻辑
- 策略模式基础:不同算法封装成子类,统一用父类引用切换行为
- 依赖倒置体现:业务模块不依赖具体实现,只依赖抽象(接口或父类)
- 便于单元测试:可传入轻量 Mock 子类,绕过数据库、网络等外部依赖
它能做什么,不能做什么?
父类引用是一扇“安全窗口”,只暴露契约约定的部分:
- ✅ 能调用父类中声明、且被子类重写的方法(运行时执行子类版本)
- ✅ 能访问父类的 public / protected 字段(但应尽量封装,避免直接访问)
- ❌ 不能调用子类独有的方法(如
Dog.bark()),除非向下转型 - ❌ 不能访问子类新增字段(编译直接报错)
- ❌ 静态方法、
final方法、private方法不参与多态(绑定在编译期)
换句话说,它强制你面向契约编程——只依赖“约定好的能力”,而不是“某个具体怎么做”。

















