父类引用指向子类对象实现动态绑定,需满足三条件:存在继承关系、子类重写非static/private/final的实例方法、通过父类引用调用;JVM依据对象实际类型查虚方法表执行对应子类方法。

父类引用指向子类对象,本质是“用统一类型操作不同具体实现”的设计落地。它不是语法技巧,而是编译期检查 + 运行时决策的协作结果:编译时只认左边声明的类型(比如 Animal),运行时才看右边实际创建的对象(比如 Dog),从而让同一句 a.sound() 在不同实例上调用出不同行为。
动态绑定怎么发生的
只有满足条件的方法才会触发动态绑定:
- 方法必须是非 static、非 private、非 final 的实例方法
- 子类必须重写了该方法(@Override)
- 调用发生在父类引用上,且该引用实际指向子类实例(如
Animal a = new Dog();)
JVM 底层靠虚方法表(vtable)支持:每个类有一张表,存着所有可重写方法的入口地址;执行 a.sound() 时,JVM 查 a 实际对象(Dog)的 vtable,跳转到 Dog.sound() 执行。
父类引用能访问什么
它是一副“只配了父类镜片的眼镜”——看得见、调得到的,仅限于父类中定义的公开成员:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 可以调用父类声明的方法,若子类重写,则运行时执行子类版本(如
a.sound()→Dog.sound()) - 可以访问父类定义的 public / protected 字段,但值来自父类字段本身(字段不参与多态,
a.name拿的是 Animal.name 的值) - 不能直接调用子类独有的方法(如
Dog.bark()),编译报错;也不能访问子类新增字段(如Dog.loyalty)
为什么这样设计有用
核心价值是解耦与扩展,让代码更稳定、更易生长:
- 方法参数用父类类型:一个
feed(Animal pet)能接收Dog、Cat、未来新加的Bird,无需修改签名或加 if-else - 集合统一管理:
List<Animal> zoo = Arrays.asList(new Dog(), new Cat());遍历时直接zoo.forEach(Animal::sound) - 替换实现零感知:把
new ArrayList<>()换成new LinkedList<>(),只要都实现List,上层逻辑完全不动
容易踩的坑
边界不清就会出问题,关键要分清“编译看左边,运行看右边”:
-
字段没有多态性:父类和子类都有同名字段(如
String type),通过父类引用访问,取的是父类字段的值 -
静态方法不走动态绑定:
Animal.sleep()和Dog.sleep()是各自类的独立方法,调用取决于引用声明类型,不是实际对象 -
强制转型要验证:想用
(Dog) a调子类独有方法?先用a instanceof Dog判断,否则可能抛ClassCastException

















