java方法调用分两阶段:编译期静态分派决定重载(依据参数声明类型选签名),运行期动态分派决定重写(依据对象实际类型选实现);二者顺序执行、互不干扰。

Java 中方法调用时的重载与重写,不是由 JVM 单一“拍板”决定的,而是靠两个独立又衔接的阶段完成:编译期静态分派处理重载,运行期动态分派处理重写。
静态分派决定重载选哪个方法签名
编译器只看参数的声明类型(静态类型),不关心对象实际是什么类。它根据变量声明时的类型、参数个数、顺序和类型,从当前作用域所有可见的同名方法中,选出最匹配的一个重载版本。
- 例如:
Animal a = new Dog(); a.speak("hi");,编译器只认a是Animal类型,就在Animal类及其父类中找speak(String)的定义,确定调用这个签名 - 即使
a实际是Dog,只要Dog没有新增speak(String)这个新重载(仅重写),就不会影响编译期选择 - 返回值、异常、访问修饰符不同,不影响重载匹配;泛型擦除后类型相同(如
List<string></string>和List<integer></integer>)可能造成意外重载匹配
动态分派决定重写执行谁的代码体
一旦静态分派锁定了方法签名(比如 speak(String)),JVM 在运行时才开始查这个签名在哪个类里真正被实现。它顺着对象的实际类型(运行时类型)向上查找继承链,优先使用子类中同签名的重写方法。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 继续上面例子:
a.speak("hi")编译锁定Animal.speak(String)签名,但运行时发现a实际是Dog,且Dog重写了该方法,就执行Dog.speak(String) - 只有非
private、非static、非final的实例方法才参与动态分派 - 底层依赖
invokevirtual指令和每个类的虚方法表(vtable),表中存的是各方法最新实现的入口地址
重载与重写共存时,两步不能跳过任何一步
一个调用同时涉及重载和重写,必须严格按顺序走完两个阶段:先静态分派选签名,再动态分派选实现。二者互不干扰,也不合并判断。
-
Animal a = new Dog(); a.speak();和a.speak("x")是两个完全不同的重载签名,各自走一遍静态分派;每个签名又各自走一遍动态分派 - 子类可以只重写部分重载版本,不影响其他重载版本的动态分派流程
- 如果某重载签名在父类中不存在,编译直接报错,根本不会走到运行期
常见误区提醒
容易混淆的地方往往出在“引用类型 vs 实际类型”的误判上。
-
Object obj = new String("s"); print(obj);调用的是print(Object),不是print(String)—— 因为obj静态类型是 static 方法看似能“重写”,实则是隐藏(hiding),调用目标由引用类型决定,属于静态分派范畴-
private方法在子类中声明同名方法,只是新方法,不参与任何分派机制
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










