一句话核心总结
- 重载:在同一个类中,方法名相同,但参数列表不同(参数个数、类型或顺序不同),它发生在编译时。
- 重写:在子类中,方法名、参数列表和返回类型与父类完全相同(返回类型可以是协变的),并且访问权限不能更严格,它发生在运行时,与多态密切相关。
详细对比表格
为了更直观地理解,我们可以通过一个对比表格来看它们的区别:

| 特征 | 重载 | 重写 |
|---|---|---|
| 定义位置 | 同一个类中,或者在同一类中的父子类也可以(但通常不这么做)。 | 子类和父类之间。 |
| 方法名 | 必须相同。 | 必须相同。 |
| 参数列表 | 必须不同(参数个数、类型或顺序至少有一项不同)。 | 必须相同。 |
| 返回类型 | 可以相同,也可以不同。如果参数列表也相同,返回类型不同会导致编译错误。 | 通常相同,在 Java 5+ 中,可以是协变返回类型(即子类方法的返回类型是父类方法返回类型的子类)。 |
| 异常 | 对抛出的异常没有特殊要求。 | 子类方法声明抛出的异常不能比父类方法更宽泛(即不能是父类方法异常的父类或同级)。 |
| 访问修饰符 | 对访问修饰符没有特殊要求。 | 子类方法的访问修饰符不能比父类方法的更严格(父类是 public,子类不能是 protected)。 |
| 绑定时机 | 编译时,编译器根据调用时传入的参数列表来决定具体调用哪个方法。 | 运行时,JVM 根据对象的实际类型(而不是引用类型)来决定调用哪个方法。 |
| 与多态的关系 | 与多态无关,它只是提供了一种在同一个类中定义多个同名方法的便利。 | 是多态的体现,没有重写,就没有运行时多态。 |
| 目的 | 功能类似,但参数不同。print(String s) 和 print(int i),实现同一个类中多种行为的“重载”。 |
子类需要修改或扩展父类的行为,实现基于对象类型的动态方法调用。 |
代码示例
通过代码示例可以更好地理解这两个概念。
重载 示例
在同一个类中,我们定义了多个 calculate 方法,它们的方法名相同,但参数列表不同。
class Calculator {
// 重载方法 1: 两个整数相加
public int calculate(int a, int b) {
System.out.println("计算两个整数的和");
return a + b;
}
// 重载方法 2: 三个整数相加
public int calculate(int a, int b, int c) {
System.out.println("计算三个整数的和");
return a + b + c;
}
// 重载方法 3: 两个浮点数相加
public double calculate(double a, double b) {
System.out.println("计算两个双精度浮点数的和");
return a + b;
}
// 这个方法会编译错误,因为它与第一个 calculate 方法签名完全相同
// public int calculate(int x, int y) { ... }
}
public class OverloadExample {
public static void main(String[] args) {
Calculator calc = new Calculator();
// 编译器根据传入的参数列表,自动选择调用哪个方法
int sum1 = calc.calculate(10, 20); // 调用 calculate(int, int)
int sum2 = calc.calculate(10, 20, 30); // 调用 calculate(int, int, int)
double sum3 = calc.calculate(10.5, 20.5); // 调用 calculate(double, double)
System.out.println("结果1: " + sum1);
System.out.println("结果2: " + sum2);
System.out.println("结果3: " + sum3);
}
}
输出:
计算两个整数的和
计算三个整数的和
计算两个双精度浮点数的和
结果1: 30
结果2: 60
结果3: 31.0
重写 示例
我们有一个父类 Animal 和一个子类 Dog,子类 Dog 重写了父类 Animal 的 makeSound 方法。

// 父类
class Animal {
// 父类的方法
public void makeSound() {
System.out.println("动物发出声音");
}
}
// 子类
class Dog extends Animal {
// @Override 注解可以帮助检查是否正确地重写了父类方法
@Override
public void makeSound() {
System.out.println("汪汪汪!");
}
}
public class OverrideExample {
public static void main(String[] args) {
// 创建一个 Animal 对象
Animal myAnimal = new Animal();
myAnimal.makeSound(); // 调用 Animal 类的 makeSound 方法
// 创建一个 Dog 对象
Dog myDog = new Dog();
myDog.makeSound(); // 调用 Dog 类重写后的 makeSound 方法
// 关键部分:多态
// 父类引用指向子类对象
Animal myPuppy = new Dog();
// 运行时,JVM 发现 myPuppy 的实际类型是 Dog,所以调用 Dog 的 makeSound 方法
myPuppy.makeSound(); // 输出 "汪汪汪!"
}
}
输出:
动物发出声音
汪汪汪!
汪汪汪!
注意最后一行 myPuppy.makeSound(); 的输出,虽然 myPuppy 的类型是 Animal,但它引用的实际对象是 new Dog(),在运行时,JVM 会根据对象的实际类型(Dog)来查找并调用 makeSound 方法,这就是运行时多态,而重写是实现它的关键。
常见误区
-
重载和返回类型有关吗?
- 无关,仅当参数列表也相同时,返回类型不同才构成编译错误,如果参数列表不同,返回类型可以自由变化。
-
子类重写父类方法时,可以降低访问权限吗?
(图片来源网络,侵删)- 不可以,父类方法是
public,子类重写时不能是protected或private,这会破坏多态的封装性。
- 不可以,父类方法是
-
private方法能被重写吗?- 不能。
private方法在声明它的类之外是不可见的,因此子类无法继承它,更谈不上重写,如果子类中定义了一个与父类private方法同名的方法,这只是一个新的、无关的方法,不是重写。
- 不能。
-
static方法能被重写吗?- 不能。
static方法是与类绑定的,而不是与实例对象绑定的,子类中定义一个与父类static方法同名的方法,这被称为方法隐藏,而不是重写,通过子类引用调用时,会调用子类的static方法;通过父类引用调用时,会调用父类的static方法。
- 不能。
记住这个简单的区分方法:
- 重载看“参数”:在同一个类里,方法名一样,参数不一样。
- 重写看“父类”:在子类里,把父类的方法“重新写一遍”,要求方法签名(方法名+参数列表)完全一样。
理解重载和重写的区别,是掌握 Java 面向对象编程,特别是多态机制的基础。
