父类引用指向子类对象是多态落地的唯一入口,如animal a = new dog();,需满足继承/实现、方法重写、向上转型三条件,jvm通过动态绑定在运行时调用子类重写方法,实现解耦与开闭原则。

父类引用指向子类对象,不是语法糖,而是多态落地的唯一入口。它让“调用逻辑”和“具体实现”真正解耦——你写的代码只认父类类型,运行时才决定执行哪个子类的方法。
这到底在代码里长什么样
它就长这样:
- Animal a = new Dog(); —— 声明是 Animal 类型,实际创建的是 Dog 实例
-
List
list = new ArrayList(); —— 接口引用指向具体实现类,日常开发中天天写 - Runnable task = () -> System.out.println("run"); —— 函数式接口也是同一原理
关键不在“能不能写”,而在于:只要满足继承/实现 + 方法重写 + 向上转型,JVM 就会在运行时自动查到子类的实际方法,这个过程叫动态绑定。
为什么非得用父类引用,不能直接 new 子类?
直接用子类变量(如 Dog d = new Dog();)没问题,但一扩展就卡住:
- 加个 Cat,就得改所有调用处;加个 Duck,又得改一遍
- 测试难 mock,比如想临时换成 MockDog,就得改入参类型或重构方法签名
- 业务逻辑和具体实现绑死,比如支付模块写死 WechatPay pay = new WechatPay();,后续接入支付宝就得动业务代码
而用 Payment pay = new WechatPay();,后续换 Alipay 只需改这一行,甚至通过配置或工厂注入,业务层完全无感。
它怎么支撑起重构和设计模式
这不是炫技,是工程演进的基础设施:
- 开闭原则落地:新增子类(如新动物、新支付方式)不用改已有调用逻辑
- 策略模式基础:不同算法封装成子类,统一用父类引用切换行为
- 依赖倒置体现:高层模块(业务)不依赖低层模块(具体实现),都依赖抽象(父类或接口)
- 便于单元测试:可传入轻量 Mock 子类,绕过数据库、网络等外部依赖
注意边界:它能做什么,不能做什么
父类引用是“安全视窗”,只能看到父类定义的部分:
- ✅ 能调用父类中声明、且被子类重写的方法(动态执行子类版)
- ✅ 能访问父类的 public / protected 字段(不推荐,应封装)
- ❌ 不能调用子类独有的方法(如 Dog.bark(),除非向下转型)
- ❌ 不能访问子类新增字段(编译直接报错)
- ❌ 静态方法、final 方法、private 方法不参与多态(它们绑定在编译期)
换句话说:它强制你面向契约编程——只依赖“约定好的能力”,而不是“某个具体怎么做”。











