目录
[1. 核心定义](#1. 核心定义)
[2. 设计思想](#2. 设计思想)
[3. 核心区别速查表(面试必考)](#3. 核心区别速查表(面试必考))
[二、底层实现原理(含 JDK 源码分析 / 反编译验证)](#二、底层实现原理(含 JDK 源码分析 / 反编译验证))
[1. 重载底层:静态绑定(编译期绑定)](#1. 重载底层:静态绑定(编译期绑定))
[2. 重写底层:动态绑定(运行期绑定)](#2. 重写底层:动态绑定(运行期绑定))
[3. 反编译验证](#3. 反编译验证)
[4. JDK 源码示例](#4. JDK 源码示例)
[1. 方法重载(基础用法)](#1. 方法重载(基础用法))
[2. 方法重写(基础用法)](#2. 方法重写(基础用法))
[坑点 1:仅参数名不同,误以为是重载](#坑点 1:仅参数名不同,误以为是重载)
[坑点 2:重写时缩小方法访问权限](#坑点 2:重写时缩小方法访问权限)
[坑点 3:静态方法尝试重写](#坑点 3:静态方法尝试重写)
[坑点 4:重载依赖返回值类型](#坑点 4:重载依赖返回值类型)
[坑点 5:重写抛出更大异常](#坑点 5:重写抛出更大异常)
[坑点 6:忽略 @Override 注解](#坑点 6:忽略 @Override 注解)
[1. 重载和重写的核心区别?(必考题)](#1. 重载和重写的核心区别?(必考题))
[2. 什么是静态绑定和动态绑定?](#2. 什么是静态绑定和动态绑定?)
[3. 为什么重载不能根据返回值区分?](#3. 为什么重载不能根据返回值区分?)
[4. @Override 注解的作用?](#4. @Override 注解的作用?)
[5. 重写的「两同两小一大」具体指什么?](#5. 重写的「两同两小一大」具体指什么?)
[6. 为什么说重载不是真正的多态?](#6. 为什么说重载不是真正的多态?)
[六、项目改造 / 落地记录](#六、项目改造 / 落地记录)
[1. 改造前(错误用法)](#1. 改造前(错误用法))
[2. 改造后(企业标准实践)](#2. 改造后(企业标准实践))
[3. 改造落地好处](#3. 改造落地好处)
一、核心定义与设计思想
1. 核心定义
(1)方法重载(Overload)
- 定义 :同一个类中 ,方法名称相同 ,参数列表不同(个数 / 类型 / 顺序)的多个方法;
- 核心规则 :两同一不同
- 两同:方法名、所属类相同;
- 一不同:参数列表(参数个数、参数类型、参数顺序)必须不同;
- 无关项:返回值类型、修饰符、参数名 不影响重载判断。
(2)方法重写(Override)
- 定义 :子类继承父类 后,重新实现父类同名、同参数列表的方法,覆盖父类逻辑;
- 核心规则 :两同两小一大 (面试死记)
- 两同:方法名、参数列表完全相同;
- 两小:子类返回值类型 ≤ 父类、子类抛出异常 ≤ 父类;
- 一大:子类方法访问权限 ≥ 父类。
2. 设计思想
- 重载 :编译期多态(静态多态) ,简化方法调用,提高 API 易用性(如
System.out.println()重载所有类型); - 重写 :运行期多态(动态多态),扩展父类功能,不修改父类代码,符合开闭原则。
3. 核心区别速查表(面试必考)
| 对比维度 | 方法重载(Overload) | 方法重写(Override) |
|---|---|---|
| 位置 | 同一个类中 | 父子类 / 接口实现中 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须不同 | 必须完全相同 |
| 返回值 | 无要求 | 协变(子类≤父类) |
| 访问权限 | 无要求 | 子类≥父类(不能缩小) |
| 异常 | 无要求 | 子类≤父类 |
| 绑定时机 | 编译期(静态绑定) | 运行期(动态绑定) |
| 设计目的 | 统一方法名,简化调用 | 扩展 / 替换父类逻辑 |
二、底层实现原理(含 JDK 源码分析 / 反编译验证)
1. 重载底层:静态绑定(编译期绑定)
- 原理 :Java 编译器根据方法签名(方法名 + 参数列表) ,在编译阶段直接确定调用哪个重载方法,写入字节码;
- 方法签名 :JVM 唯一标识方法的依据,不包含返回值;
- 绑定指令 :
invokevirtual编译期直接锁定目标方法。
2. 重写底层:动态绑定(运行期绑定)
- 原理 :编译期仅记录父类方法,运行期 JVM 根据实际对象类型 ,查询类方法表,调用子类重写方法;
- 核心依赖:继承 + 向上转型;
- JVM 机制:每个类加载时生成方法表,存储重写方法的内存地址。
3. 反编译验证
(1)重载反编译(静态绑定)
java
public class OverloadTest {
public void test(int a) {}
public void test(String a) {}
public static void main(String[] args) {
new OverloadTest().test(1);
}
}
命令:javap -c OverloadTest
java
0: invokevirtual #4 // Method test:(I)V 编译期直接确定调用int参数方法
(2)重写反编译(动态绑定)
java
class Parent{ public void test(){} }
class Child extends Parent{ @Override public void test(){} }
public class OverrideTest {
public static void main(String[] args) {
Parent p = new Child();
p.test();
}
}
命令:javap -c OverrideTest
java
0: invokevirtual #5 // Method Parent.test:()V 编译期标记父类,运行期绑定子类
4. JDK 源码示例
重载:String 类大量重载方法
java
public final class String {
public boolean startsWith(String prefix) {}
public boolean startsWith(String prefix, int offset) {} // 重载:参数个数不同
}
重写:Object equals () 被 String 重写
java
public boolean equals(Object anObject) { // 重写Object的equals
// 子类实现逻辑
}
三、代码示例
1. 方法重载(基础用法)
java
/**
* 重载演示:同一个类,方法名相同,参数不同
*/
public class OverloadDemo {
// 1. 无参方法
public void sum() {
System.out.println(0);
}
// 2. 重载:int参数
public void sum(int a) {
System.out.println(a);
}
// 3. 重载:两个int参数
public void sum(int a, int b) {
System.out.println(a + b);
}
// 4. 重载:参数类型不同
public void sum(double a, double b) {
System.out.println(a + b);
}
public static void main(String[] args) {
OverloadDemo demo = new OverloadDemo();
demo.sum(); // 调用无参
demo.sum(10); // 调用int单参
demo.sum(1,2); // 调用int双参
}
}
2. 方法重写(基础用法)
java
// 父类
class Animal {
public void shout() {
System.out.println("动物叫");
}
}
// 子类:重写父类方法
class Dog extends Animal {
// @Override 注解:校验是否符合重写规则
@Override
public void shout() {
System.out.println("汪汪汪");
}
}
public class OverrideDemo {
public static void main(String[] args) {
Animal animal = new Dog(); // 向上转型
animal.shout(); // 运行期调用子类重写方法
}
}
四、高频踩坑点与避坑方案
坑点 1:仅参数名不同,误以为是重载
- 问题:
test(int a)和test(int b)不算重载,编译报错; - 原因:重载只看参数类型 / 个数 / 顺序,参数名无关;
- 避坑:修改参数列表,而非参数名。
坑点 2:重写时缩小方法访问权限
- 问题:父类
public方法,子类写private,编译报错; - 规则:子类权限只能放大 / 不变,不能缩小;
- 避坑:严格遵守一大原则。
坑点 3:静态方法尝试重写
- 问题:父子类静态方法同名,看似重写,实际是方法隐藏;
- 原因:静态方法静态绑定,无多态;
- 避坑:静态方法不存在重写,不要用
@Override。
坑点 4:重载依赖返回值类型
- 问题:
int test()和void test()不算重载; - 原因:方法签名不包含返回值,编译器无法区分;
- 避坑:重载必须修改参数列表。
坑点 5:重写抛出更大异常
- 问题:父类无异常,子类抛出
Exception,编译报错; - 规则:子类异常≤父类异常;
- 避坑:子类只能抛出父类异常的子类,或不抛异常。
坑点 6:忽略 @Override 注解
- 问题:拼写错误导致不是重写,编译不报错;
- 避坑:重写必须加 @Override,编译器自动校验规则。
五、面试高频考点与标准答案
1. 重载和重写的核心区别?(必考题)
标准答案:
- 位置 :重载在同一个类 ,重写在父子类;
- 参数 :重载参数必须不同 ,重写参数必须相同;
- 绑定 :重载编译期静态绑定 ,重写运行期动态绑定;
- 多态 :重载是静态多态 ,重写是动态多态;
- 规则 :重载无权限 / 返回值限制,重写遵守两同两小一大。
2. 什么是静态绑定和动态绑定?
标准答案:
- 静态绑定 :编译期确定方法调用,如重载、private/static/final 方法;
- 动态绑定 :运行期根据实际对象类型确定方法调用,如重写。
3. 为什么重载不能根据返回值区分?
标准答案 :JVM 的方法签名不包含返回值,编译器无法通过返回值唯一确定方法,因此返回值不能作为重载依据。
4. @Override 注解的作用?
标准答案 :编译期校验方法是否符合重写规则,若不符合直接编译报错,避免手写错误。
5. 重写的「两同两小一大」具体指什么?
标准答案:
- 两同:方法名、参数列表相同;
- 两小:子类返回值类型≤父类,子类抛出异常≤父类;
- 一大:子类访问权限≥父类。
6. 为什么说重载不是真正的多态?
标准答案 :重载是编译期语法糖 ,在运行时无多态特性;真正的多态指运行期动态绑定的重写。
六、项目改造 / 落地记录
核心规范:工具类用重载,业务类用重写,面向接口编程
1. 改造前(错误用法)
java
// 1. 错误重载:参数名不同,无效
public void test(int a){}
public void test(int b){}
// 2. 错误重写:权限缩小,编译报错
class Parent{ public void run(){} }
class Child{ private void run(){} }
// 3. 冗余方法:无重载,调用繁琐
public void printInt(int a){}
public void printString(String s){}
2. 改造后(企业标准实践)
(1)工具类:使用重载简化调用
java
public final class StringUtils {
// 重载:统一方法名,适配不同参数
public static boolean isEmpty(String s) {}
public static boolean isEmpty(Object obj) {}
public static boolean isEmpty(Collection<?> c) {}
}
(2)业务类:使用重写扩展功能
java
// 接口定义规范
public interface UserService {
void addUser();
}
// 实现类重写接口方法
public class UserServiceImpl implements UserService {
@Override
public void addUser() {
// 业务实现
}
}
3. 改造落地好处
- 易用性:重载统一方法名,降低调用成本;
- 扩展性:重写不修改父类,符合开闭原则;
- 可读性:代码规范统一,团队协作无歧义;
- 框架兼容:Spring/MyBatis 底层大量依赖重写(接口实现)。
总结
- 核心区分:重载 = 同类同参异 = 静态绑定;重写 = 父子同参同 = 动态绑定;
- 规则死记 :重载看参数列表,重写守两同两小一大;
- 避坑铁律 :参数名≠重载,静态方法无重写,重写必加
@Override; - 实战规范:工具类用重载提效,业务继承用重写扩展。