0%

java重载与重写

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 {
// 1. 一个参数的加法
public int add(int a) {
return a;
}

// 2. 两个int参数的加法(参数个数不同)
public int add(int a, int b) {
return a + b;
}

// 3. 两个double参数的加法(参数类型不同)
public double add(double a, double b) {
return a + b;
}

// 4. 参数顺序不同(int在前 vs String在前)
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)); // 调用1
System.out.println(calc.add(1, 2)); // 调用2
System.out.println(calc.add(1.5, 2.5)); // 调用3
System.out.println(calc.add(3, "个")); // 调用4
System.out.println(calc.add("共", 5)); // 调用5
}
}
  • 执行结果:编译器根据传入的参数类型和个数,自动匹配对应的重载方法,输出正确结果。

重载的应用场景

  • 简化方法命名:用相同名称表示语义相似的操作(如 add 可表示不同参数的加法)。
  • 提高代码可读性:避免为相似功能起不同名称(如 addIntaddDouble),减少记忆负担。

重写(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 {
// 重写父类的eat方法(访问权限扩大:protected → public)
@Override
public void eat() {
System.out.println("狗吃骨头");
}

// 重写父类的move方法(返回值相同,参数相同)
@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(); // 调用子类重写的eat(多态)→ 输出"狗吃骨头"
animal.move(); // 调用子类重写的move → 输出"狗跑"
animal.sleep(); // 调用父类静态方法(无多态)→ 输出"动物睡觉"
}
}
  • @Override 注解:显式标记方法为重写,编译器会检查是否符合重写规则,避免误写(如参数错误)。

重写的应用场景

  • 多态实现:通过父类引用调用子类重写的方法,实现 “同一接口,不同实现”。
  • 功能扩展:在保留父类核心逻辑的基础上,子类可通过 super 调用父类方法并添加新逻辑。
1
2
3
4
5
@Override
public void eat() {
super.eat(); // 调用父类的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); // 声明类型为Parent → 输出"父类字段"

Child c = new Child();
System.out.println(c.name); // 声明类型为Child → 输出"子类字段"
}
}
  • 注意:字段隐藏是不良实践,通常应将字段设为 private 并用 getter 方法访问,避免歧义。

重载与重写的核心区别

维度 重载(Overload) 重写(Override)
定义 同一类中,方法名相同,参数列表不同 子类中,方法名和参数列表与父类完全相同
绑定时机 编译期绑定(静态绑定) 运行期绑定(动态绑定)
多态性 不体现多态(仅编译期区分) 体现多态(父类引用调用子类实现)
权限修饰符 无限制 子类权限 ≥ 父类权限
返回值类型 无限制(与重载无关) 基本类型必须相同;引用类型可协变
异常 无限制 子类异常 ≤ 父类异常(范围和数量)
适用范围 同一类或父子类(但通常在同一类) 仅父子类
静态方法 可重载 不可重写(仅能隐藏)

欢迎关注我的其它发布渠道