重载由编译期静态分派决定,依据参数静态类型选择方法签名;重写由运行期动态分派决定,依据对象实际类型选择具体实现;二者先后协作,互不干扰。

当重载与重写同时存在时,JVM 并不直接“抉择”方法签名——真正起决定作用的是两个独立阶段:编译期静态分派(处理重载),和运行期动态分派(处理重写)。两者分工明确,互不干扰。
编译期:靠参数的静态类型选重载版本
Java 编译器根据调用处传入参数的声明类型(即变量声明时的类型)和数量、顺序,匹配当前作用域内所有可见的同名方法,选出唯一一个最匹配的重载方法。这个过程发生在编译阶段,生成的字节码中已固定调用哪个方法符号(如 invokestatic 或 invokevirtual 指令 + 方法引用)。
- 例如:
Animal a = new Dog(); a.speak("hello");,编译器只看a的声明类型Animal,再结合字符串参数,在Animal类及其父类中查找所有speak(String)的重载候选,最终确定调用哪一个重载签名 - 返回值、异常、修饰符不影响重载选择
- 如果找不到匹配的重载,编译失败
运行期:靠对象的实际类型选重写实现
一旦编译确定了要调用的方法签名(比如 speak(String)),JVM 在运行时会检查该调用目标是否被重写。它顺着对象的实际运行时类型(如 new Dog())向上查找,优先使用子类中定义的同签名方法;若子类没有,则沿继承链向上找,直到找到第一个匹配的实现。
- 这个过程依赖 invokevirtual 指令和虚方法表(vtable)机制
- 只有非 private、非 static、非 final 的实例方法才参与动态分派
- 方法签名(名称 + 参数类型)必须完全一致,否则不算重写,只是新方法
关键点:重载和重写解决的是不同问题
重载解决“该调用哪个方法名+参数组合”,是编译期绑定;重写解决“同一个签名下,到底执行谁的代码体”,是运行期绑定。它们不是竞争关系,而是先后协作:
立即学习“Java免费学习笔记(深入)”;
- 先由编译器锁死“调用哪个重载签名”
- 再由 JVM 在运行时决定“该签名对应哪个类的具体实现”
- 哪怕子类重写了多个重载版本(如同时重写
speak()、speak(String)、speak(String, int)),每个版本仍各自走自己的动态分派流程
一个典型例子
假设:class Animal { void speak() { } void speak(String s) { } }class Dog extends Animal { @Override void speak() { System.out.println("Woof!"); } @Override void speak(String s) { System.out.println("Woof: " + s); } }
调用 Animal a = new Dog(); a.speak("hi");:
→ 编译期:根据 a 是 Animal 类型 + 参数 String,选定 Animal.speak(String) 这个签名
→ 运行期:发现实际对象是 Dog,且 Dog 重写了该签名,于是执行 Dog.speak(String)


















