执行子类重写版本,完全由对象实际类型(new后面的类)决定;编译期只检查父类是否有该方法,运行时JVM根据真实对象类型查虚方法表动态绑定,前提为方法非static、非private、非final且被子类@Override重写。

父类引用调用重写方法时,执行哪个版本,**完全由对象的实际类型(即 new 后面的那个类)决定,而不是引用变量的声明类型**。这是 Java 运行时多态的核心机制,依赖 JVM 的动态绑定(Dynamic Binding)。
关键判断依据:运行看右边
所谓“编译看左边,运行看右边”,意思是:
- 编译阶段只检查引用类型(左边)是否定义了该方法——只要父类有这个方法,就能通过编译;
- 运行阶段 JVM 查看该引用**实际指向的对象是哪个类的实例**(右边),然后调用对应子类中重写后的方法。
例如:
Animal a = new Dog();
a.makeSound();
虽然 a 声明为 Animal 类型,但实际是 Dog 实例,所以调用的是 Dog 类中的 makeSound()。
前提条件缺一不可
只有同时满足以下三点,才能触发重写方法的动态分派:
- 存在继承关系(Dog extends Animal);
- 子类确实重写了父类的非 private、非 static、非 final 方法;
- 通过父类引用(如 Animal a)调用该方法,且该引用实际指向子类对象(如 new Dog())。
为什么 private / static / final 方法不参与重写
这些方法在编译期就绑定了目标实现,无法被动态替换:
立即学习“Java免费学习笔记(深入)”;
- private:仅在本类可见,子类根本“看不到”,谈不上重写;
- static:属于类,不依赖对象实例,调用取决于引用类型(即“编译看左边”);
- final:明确禁止重写,JVM 直接内联或静态绑定。
如何确认是否真的发生了重写调用
推荐两个实用做法:
- 在子类重写方法上加上 @Override 注解——编译器会帮你校验签名是否匹配父类;
- 在方法内部打印 getClass().getSimpleName(),比如 System.out.println("Called from " + this.getClass().getSimpleName());,运行时可直观看到实际执行的是哪个类的逻辑。


















