Java 中的重载(Overload)与重写(Override)详解
在 Java 中,重载(Overload)和重写(Override)是两个容易混淆但意义完全不同的概念。它们都是实现代码复用和多态性的重要手段,但应用场景和规则截然不同。本文将从定义、规则、示例及区别等方面,全面解析两者的核心差异。
重载(Overload):同一类中的同名方法
重载指在同一个类中,存在多个方法名称相同但参数列表不同的方法。编译器通过参数列表的差异来区分不同的方法,与返回值类型、异常类型无关。
重载的核心规则
- 方法名称必须相同:如
add(int a) 和 add(int a, int b) 是重载。
- 参数列表必须不同:
- 参数个数不同(如
add(int a) vs add(int a, int b));
- 参数类型不同(如
add(int a) vs add(double a));
- 参数顺序不同(如
add(int a, String b) vs add(String a, int b))。
- 与返回值类型无关:仅返回值不同不能构成重载(如
int add(int a) vs double add(int a) 会编译报错)。
- 与访问修饰符、异常类型无关:修饰符或异常不同不影响重载判断,只要参数列表不同即可。
示例:方法重载
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| public class Calculator { public int add(int a) { return a; }
public int add(int a, int b) { return a + b; }
public double add(double a, double b) { return a + b; }
public String add(int a, String b) { return b + a; }
public String add(String a, int b) { return a + b; }
public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println(calc.add(1)); System.out.println(calc.add(1, 2)); System.out.println(calc.add(1.5, 2.5)); System.out.println(calc.add(3, "个")); System.out.println(calc.add("共", 5)); } }
|
- 执行结果:编译器根据传入的参数类型和个数,自动匹配对应的重载方法,输出正确结果。
重载的应用场景
- 简化方法命名:用相同名称表示语义相似的操作(如
add 可表示不同参数的加法)。
- 提高代码可读性:避免为相似功能起不同名称(如
addInt、addDouble),减少记忆负担。
重写(Override):子类覆盖父类方法
重写指子类中定义的方法与父类中的某个方法名称、参数列表完全相同,从而覆盖父类方法的实现。重写是面向对象多态性的核心体现,允许子类根据自身需求修改父类的行为。
重写的核心规则(“两同两小一大”)
- 方法名称和参数列表必须完全相同:与父类方法的名称、参数类型、顺序、个数完全一致(否则为重载)。
- 返回值类型:
- 基本类型:必须与父类完全相同;
- 引用类型:子类返回值可以是父类返回值的子类(协变返回类型,JDK 5+ 支持)。
- 访问修饰符:子类方法的访问权限不能小于父类方法(如父类为
protected,子类可改为 public,但不能改为 private)。
- 异常类型:子类方法抛出的异常不能多于或宽于父类方法(可抛出更少或更具体的异常)。
- 静态方法不能重写:子类若定义与父类同名的静态方法,属于 “隐藏” 而非重写(编译不会报错,但不具备多态性)。
- 私有方法不能重写:父类的
private 方法对子类不可见,子类定义同名方法视为全新方法。
示例:方法重写
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| class Animal { protected void eat() { System.out.println("动物吃东西"); }
public void move() { System.out.println("动物移动"); }
public static void sleep() { System.out.println("动物睡觉"); } }
class Dog extends Animal { @Override public void eat() { System.out.println("狗吃骨头"); }
@Override public void move() { System.out.println("狗跑"); }
public static void sleep() { System.out.println("狗睡觉"); } }
public class TestOverride { public static void main(String[] args) { Animal animal = new Dog(); animal.eat(); animal.move(); animal.sleep(); } }
|
@Override 注解:显式标记方法为重写,编译器会检查是否符合重写规则,避免误写(如参数错误)。
重写的应用场景
- 多态实现:通过父类引用调用子类重写的方法,实现 “同一接口,不同实现”。
- 功能扩展:在保留父类核心逻辑的基础上,子类可通过
super 调用父类方法并添加新逻辑。
1 2 3 4 5
| @Override public void eat() { super.eat(); System.out.println("狗吃完骨头舔爪子"); }
|
隐藏字段(Field Hiding)
与方法不同,父类和子类的同名实例变量不会发生重写,而是 “隐藏”—— 子类的字段会覆盖父类的字段,但不具备多态性,访问时由声明类型决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| class Parent { public String name = "父类字段"; }
class Child extends Parent { public String name = "子类字段"; }
public class TestField { public static void main(String[] args) { Parent p = new Child(); System.out.println(p.name);
Child c = new Child(); System.out.println(c.name); } }
|
- 注意:字段隐藏是不良实践,通常应将字段设为
private 并用 getter 方法访问,避免歧义。
重载与重写的核心区别
| 维度 |
重载(Overload) |
重写(Override) |
| 定义 |
同一类中,方法名相同,参数列表不同 |
子类中,方法名和参数列表与父类完全相同 |
| 绑定时机 |
编译期绑定(静态绑定) |
运行期绑定(动态绑定) |
| 多态性 |
不体现多态(仅编译期区分) |
体现多态(父类引用调用子类实现) |
| 权限修饰符 |
无限制 |
子类权限 ≥ 父类权限 |
| 返回值类型 |
无限制(与重载无关) |
基本类型必须相同;引用类型可协变 |
| 异常 |
无限制 |
子类异常 ≤ 父类异常(范围和数量) |
| 适用范围 |
同一类或父子类(但通常在同一类) |
仅父子类 |
| 静态方法 |
可重载 |
不可重写(仅能隐藏) |