“编译看左边,运行看右边”本质是静态绑定与动态绑定的分工:编译期按声明类型检查合法性并绑定静态方法、字段等;运行期通过对象实际类型查vtable动态绑定可重写实例方法。

Java多态中“静态方法看编译期类型,实例方法看运行期类型”,本质是绑定时机不同导致的行为差异:静态方法在编译时就锁定了调用目标,而可重写的实例方法要等到对象真正创建后,由JVM查虚方法表(vtable)决定执行哪个版本。
静态方法为什么只看左边(编译期类型)
静态方法属于类,不依赖对象实例。它随类加载而存在,内存中只有一份,且不能被重写(只能被隐藏)。编译器根据变量声明的类型(即“左边”)直接确定调用哪个类的静态方法,运行时不会切换。
- Person p = new Student(); p.print(); → 编译器查 Person 类是否有 print() 静态方法,有就绑定;即使 Student 也定义了同名静态方法,也不会被调用
- 若把 p 声明为 Student p = new Student();,则调用 Student.print() —— 完全由声明类型决定,和右边 new 的是什么无关
- 这种绑定发生在编译阶段,叫静态绑定(或早期绑定),不涉及对象实际类型
实例方法为什么运行时才定(看右边)
非 private、非 static、非 final 的实例方法支持重写,JVM 必须等程序运行、对象真实创建后,才能通过对象头中的类指针找到其实际类型,再查该类型的虚方法表,定位到最终执行的方法体。
- Animal a = new Dog(); a.sound(); → 编译时只检查 Animal 是否有 sound() 方法(确保合法)
- 运行时发现 a 实际指向 Dog 对象,Dog 类 vtable 中 sound() 指向的是 Dog 的实现,于是执行 Dog.sound()
- 这个过程叫动态绑定(或晚期绑定),是多态生效的核心机制
字段和静态方法一样,都不看运行时类型
成员变量(字段)访问永远依据引用的编译时类型,不是对象实际类型。这和静态方法一致,都属于“编译期确定”的行为。
- Animal a = new Dog(); System.out.println(a.name); → 输出 Animal 类定义的 name 值,哪怕 Dog 中也有同名字段
- 字段不参与多态,没有重写概念,只有隐藏(子类字段会遮蔽父类同名字段,但访问仍取决于引用类型)
- 所以“编译看左边”适用于:静态方法、字段、private/final/static 方法调用
关键判断口诀与前提条件
是否触发“运行看右边”,不取决于有没有继承,而取决于三点同时满足:
- 有继承或实现关系
- 调用的是非 static、非 private、非 final 的实例方法
- 使用父类(或接口)引用指向子类(或实现类)对象
缺一不可。比如 super.method() 看似在子类里,但它是显式指定父类版本,属于编译期确定;而 this.method() 在子类中调用,若 method 可被重写,依然走动态绑定——因为 this 的运行时类型仍是当前对象的实际类型。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











