父类引用指向子类对象是编译时看声明类型、运行时看实际对象的动态绑定机制,需满足继承关系、方法重写、父类声明子类实例三个前提,实现解耦与扩展。

父类引用指向子类对象,本质是“用统一类型操作不同具体实现”的设计思想落地。它不是语法糖,而是编译期与运行期协同工作的结果:编译时看声明类型(父类),运行时看实际对象(子类),从而让同一行调用产生不同行为。
为什么能这么写?——三个硬性前提
缺一不可:
- 存在继承关系(或实现接口):比如
Dog extends Animal或ArrayList implements List - 子类重写了父类的非私有、非静态方法:只有被重写的方法才能在运行时动态绑定
- 声明为父类类型,但用子类构造器创建实例:如
Animal a = new Dog();
它到底访问的是什么?——“看得见”和“用得到”的区别
父类引用就像一副只配了父类镜片的眼镜:它能“看见”父类定义的所有公开成员(字段、方法),也能“用到”其中被子类重写的方法;但它完全“看不见”子类自己新增的字段或方法。
例如:
-
a.name可以访问(假设name是Animal的 public 字段) -
a.bark()编译报错(bark()是Dog独有,Animal没声明) -
a.sound()正常执行,且实际调用的是Dog.sound()(前提是该方法在Animal中已定义并被Dog重写)
它真正解决的问题——解耦与扩展
不是为了炫技,而是让代码更易维护:
- 方法参数用父类类型:一个
feed(Animal pet)方法,日后传new Cat()、new Bird()都无需改方法签名 - 集合统一管理:用
List<animal></animal>存各种动物,遍历时统一调sound(),不用 if-else 判断类型再分发 - 替换实现不碰调用方:把
new ArrayList()换成new LinkedList(),只要都实现了List,上层逻辑零修改
常见误区提醒
属性没有多态性:如果父类和子类都有同名字段(如 String type),通过父类引用访问,拿到的是父类字段的值,不是子类的——因为字段访问由编译时类型决定,不走动态绑定。
强制转型要谨慎:从父类引用转回子类(如 (Dog) a)必须确保 a 实际确实是 Dog 实例,否则抛 ClassCastException;安全做法是先用 instanceof 判断。











