Java中方法重载与重写的区别与避坑指南
引言
在 Java 基础面试中,重载(Overload)和重写(Override)是必考知识点;在实际开发中,二者也是实现多态、代码复用、业务扩展的核心基础。
但绝大多数同学只懂皮毛,分不清底层差异,经常踩坑:把重载当成重写、静态方法误判重写、协变返回混淆、参数匹配出错、多态调用失效等问题层出不穷。
一、Java 两大多态机制
想要彻底理解重载和重写,首先要搞懂 Java 的两种多态,这是二者的本质区别:
1. 静态多态(编译期多态)
程序在编译阶段就已经确定调用哪个方法,方法绑定不可更改,重载属于静态多态。
2. 动态多态(运行期多态)
程序在运行阶段根据对象的真实类型,动态绑定对应方法,重写属于动态多态,也是 Java 面向对象的核心特性。
二、方法重载(Overload)完整详解
1. 官方定义
方法重载:在同一个类中,存在多个方法名完全相同,但参数列表不同的方法,这种现象就是方法重载。
2. 触发重载的三种条件(满足其一即可)
参数列表不同,只包含以下三种情况,除此之外,所有条件都不参与重载判定:
- 参数个数不同
- 参数类型不同
- 参数顺序不同(不同类型参数调换顺序)
3. 合法重载代码示例
public class OverloadDemo {
// 基础方法
public void test(int a) {
System.out.println("参数为int类型");
}
// 重载1:参数个数不同
public void test(int a, int b) {}
// 重载2:参数类型不同
public void test(String a) {}
// 重载3:参数顺序不同(不同类型)
public void test(int a, String b) {}
public void test(String b, int a) {}
}4. 重载判定的「四大无关项」
很多新手踩坑:返回值、访问权限、方法异常、方法修饰符,完全不参与重载判断!
仅靠这四项不同,无法构成重载,直接编译报错!
// 错误示例:仅返回值不同,不构成重载
public void demo(){}
public int demo(){ return 1; }
// 错误示例:仅权限不同,不构成重载
public void func(){}
private void func(){}5. 重载底层原理
编译阶段,编译器会根据方法名+参数列表生成唯一的方法签名,区分不同重载方法。
简单说:代码写完,编译完成,调用哪个方法就已经定死了,不存在运行时变化,这就是静态绑定。
三、方法重写(Override)详解
1. 官方定义
方法重写:发生在父子类继承关系中,子类对父类中非私有、非静态、非final的实例方法进行重新实现,保持方法签名一致,覆盖父类原有逻辑,实现子类个性化业务。
2. 重写五大硬性规则
1. 方法名、参数列表必须和父类完全一致
2. 权限修饰符:子类权限 ≥ 父类权限(只能放大,不能缩小)
3. 返回值:支持协变返回(引用类型可返回子类,基本类型必须一致)
4. 异常:子类抛出的受检异常,不能比父类更宽泛
5. 禁止重写的方法:private、static、final、构造方法
3. 合法重写代码示例
class Person {}
class Student extends Person {}
class Father {
public Person getInfo() {
return new Person();
}
public void say() throws Exception {}
}
class Son extends Father {
// 合法:协变返回,返回子类类型
@Override
public Student getInfo() {
return new Student();
}
// 合法:子类异常范围更小
@Override
public void say() throws RuntimeException {}
}4. 重写底层原理
重写基于动态绑定:编译时看父类引用,运行时看真实对象类型。
JVM 在运行时通过**虚方法表(vtable)**找到子类重写后的方法执行,这也是多态的核心实现。
四、重载 & 重写 终极对比表
对比维度 | 重载 Overload | 重写 Override |
发生范围 | 同一个类内部 | 父子类继承关系中 |
方法名 | 必须相同 | 必须相同 |
参数列表 | 必须不同 | 必须完全一致 |
返回值 | 无要求,不参与判定 | 基本类型一致,引用类型支持协变 |
权限修饰符 | 无要求 | 权限只能扩大,不能缩小 |
异常抛出 | 无要求 | 受检异常范围只能更小或相同 |
绑定时机 | 编译期静态绑定 | 运行期动态绑定 |
多态类型 | 静态多态 | 动态多态 |
注解支持 | 无专属注解 | 必须加 @Override 校验 |
五、注意事项
1:静态方法「看似重写,实则覆盖」
核心结论:静态方法不能重写,只能覆盖,多态完全失效!
class Father {
public static void show() {
System.out.println("父类静态方法");
}
}
class Son extends Father {
public static void show() {
System.out.println("子类静态方法");
}
}
// 测试调用
public class Test {
public static void main(String[] args) {
Father f = new Son();
f.show(); // 输出:父类静态方法
}
}原因:静态方法属于类,不属于对象,编译期绑定父类类型,和子类无关,不存在多态。
2:参数类型不一致,误把「重载」当「重写」
这是新手最高频错误!参数细微不同,根本不是重写,是重载!
class Father {
public void test(Object obj) {}
}
class Son extends Father {
// 不是重写!是重载!参数类型不一致
public void test(String str) {}
}避坑方案:重写必须加 @Override,编译器自动校验,杜绝此类问题。
3:private 方法不存在重写
父类私有方法对子类不可见,子类同名方法是全新方法,和重写无关。
class Father {
private void test() {
System.out.println("父类私有方法");
}
}
class Son extends Father {
public void test() {
System.out.println("子类方法");
}
}4:权限修饰符越改越小,编译报错
重写规则:子类访问权限不能比父类严格
权限等级:public > protected > 默认包访问权限 > private
5:受检异常范围扩大,重写失败
运行时异常无限制,受检异常必须子类 ≤ 父类,不能抛出更大范围异常。
6:可变参数与数组参数冲突,无法重载
可变参数本质是数组,二者视为同一个方法,不构成重载:
// 编译报错,冲突
public void fun(int[] arr){}
public void fun(int... arr){}7:仅参数名称不同,不构成重载
重载只看参数类型、个数、顺序,参数名不参与判定。
六、@Override 注解的核心价值
很多人以为它是摆设,其实是防坑神器:
1. 强制编译器校验当前方法是否为合法重写
2. 杜绝参数写错、返回值错误、静态覆盖、权限错误等所有重写问题
3. 提升代码可读性,一眼区分普通方法和重写方法
开发规范:所有重写方法,必须添加 @Override 注解!
七、总结
重载:同类、同名不同参、编译绑定、静态多态,为了灵活适配多场景调用。
重写:父子、同名同参、运行绑定、动态多态,为了子类个性化扩展。
记住:重载看参数,重写看继承;重载编译定,重写运行变。
写在最后
重载和重写看似简单,却是 Java 面向对象、多态机制的基石,也是面试和实际开发的高频考点。很多高级特性(多态调用、框架底层封装、方法扩展)都建立在这两个特性之上。
看似基础的知识点,往往最容易藏坑。基础打牢,才能走得更远,吃透这篇,彻底告别重载重写相关bug和面试短板!
以上就是Java中方法重载与重写的区别与避坑指南的详细内容,更多关于Java方法重载与重写区别的资料请关注脚本之家其它相关文章!
相关文章
详解Spring 框架中切入点 pointcut 表达式的常用写法
这篇文章主要介绍了详解Spring 框架中切入点 pointcut 表达式的常用写法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下2017-04-04
在 Spring Boot 项目中使用分页插件的两种常见方式示例详解
本文介绍了SpringBoot项目中两种分页插件的使用方法:MyBatis-Plus分页插件和PageHelper插件,结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧2025-10-10


最新评论