问题
重载与重写的区别是什么?
答案
核心概念
- 重载(Overload):同一个类中,方法名相同但参数列表不同的多个方法
- 发生在编译期(静态多态)
- 体现了同一操作的多种实现方式
- 重写(Override):子类重新定义父类中相同签名的方法
- 发生在运行期(动态多态)
- 体现了子类对父类行为的定制
1. 方法重载(Overload)
基本规则
public class Calculator {
// 方法名相同,参数列表不同
// 1. 参数个数不同
public int add(int a, int b) {
return a + b;
}
public int add(int a, int b, int c) {
return a + b + c;
}
// 2. 参数类型不同
public double add(double a, double b) {
return a + b;
}
// 3. 参数顺序不同
public void print(String str, int num) {
System.out.println(str + num);
}
public void print(int num, String str) {
System.out.println(num + str);
}
}
重载的判定条件
| 条件 | 是否影响重载 | 说明 |
|---|---|---|
| 方法名 | 必须相同 | 重载的前提 |
| 参数列表 | 必须不同 | 个数、类型或顺序至少一项不同 |
| 返回值类型 | 不影响 | 仅返回值不同不构成重载 |
| 访问修饰符 | 不影响 | public/private 不影响重载 |
| 异常声明 | 不影响 | throws 子句不影响重载 |
错误示例
public class ErrorDemo {
// 编译错误:仅返回值不同,不构成重载
public int getValue() { return 1; }
public double getValue() { return 1.0; } // 编译错误
// 编译错误:仅访问修饰符不同,不构成重载
public void show()
private void show() {} // 编译错误
}
典型应用场景
// 1. 构造方法重载
public class User {
private String name;
private int age;
public User() {}
public User(String name) {
this.name = name;
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
}
// 2. 可变参数与重载
public class VarargsDemo {
public void print(String... args) {
System.out.println("可变参数");
}
public void print(String arg) {
System.out.println("单个参数"); // 优先匹配精确类型
}
public static void main(String[] args) {
VarargsDemo demo = new VarargsDemo();
demo.print("test"); // 调用 print(String)
}
}
2. 方法重写(Override)
基本规则
public class Animal {
public void makeSound() {
System.out.println("动物发出声音");
}
public void eat(String food) {
System.out.println("动物吃" + food);
}
}
public class Dog extends Animal {
// 重写父类方法
@Override
public void makeSound() {
System.out.println("汪汪汪");
}
@Override
public void eat(String food) {
System.out.println("狗狗吃" + food);
}
}
重写的约束条件
| 条件 | 要求 | 说明 |
|---|---|---|
| 方法名 | 必须相同 | 完全一致 |
| 参数列表 | 必须相同 | 类型、个数、顺序完全一致 |
| 返回值类型 | 相同或子类型 | 协变返回类型(Java 5+) |
| 访问修饰符 | 不能更严格 | public → public/protected,不能 public → private |
| 异常声明 | 不能更宽泛 | 只能抛出父类方法异常的子类或更少异常 |
| static/final/private | 不能重写 | 这些方法不参与多态 |
协变返回类型
public class Parent {
public Number getValue() {
return 1;
}
}
public class Child extends Parent {
// 返回值类型可以是父类返回值的子类
@Override
public Integer getValue() { // Integer 是 Number 的子类
return 100;
}
}
访问修饰符规则
public class Base {
protected void show() {}
}
public class Derived extends Base {
// 正确:访问权限可以扩大
@Override
public void show() {}
// 错误:访问权限不能缩小
// @Override
// private void show() {} // 编译错误
}
异常声明规则
public class Parent {
public void method() throws IOException {
// ...
}
}
public class Child extends Parent {
// 正确:可以不抛异常
@Override
public void method() {
// ...
}
// 正确:可以抛出子类异常
@Override
public void method() throws FileNotFoundException { // IOException 的子类
// ...
}
// 错误:不能抛出更宽泛的异常
// @Override
// public void method() throws Exception { // 编译错误
// // ...
// }
}
不能重写的方法
public class Parent {
// 1. final 方法不能重写
public final void finalMethod() {}
// 2. static 方法不能重写(但可以隐藏)
public static void staticMethod() {}
// 3. private 方法不能重写(子类不可见)
private void privateMethod() {}
}
public class Child extends Parent {
// 这不是重写,而是定义新方法(方法隐藏)
public static void staticMethod() {}
}
3. 重载 vs 重写对比
| 特性 | 重载(Overload) | 重写(Override) |
|---|---|---|
| 英文名称 | Overload | Override |
| 发生范围 | 同一个类 | 父类与子类之间 |
| 方法名 | 必须相同 | 必须相同 |
| 参数列表 | 必须不同 | 必须相同 |
| 返回值类型 | 无要求 | 相同或子类型 |
| 访问修饰符 | 无要求 | 不能更严格 |
| 异常声明 | 无要求 | 不能更宽泛 |
| 绑定时机 | 编译期(静态绑定) | 运行期(动态绑定) |
| 多态类型 | 编译时多态 | 运行时多态 |
| @Override 注解 | 不适用 | 推荐使用 |
4. 多态的体现
public class PolymorphismDemo {
public static void main(String[] args) {
// 重写体现运行时多态
Animal animal = new Dog();
animal.makeSound(); // 输出:汪汪汪(运行时决定调用 Dog 的方法)
// 重载体现编译时多态
Calculator calc = new Calculator();
calc.add(1, 2); // 调用 add(int, int)
calc.add(1.0, 2.0); // 调用 add(double, double)
}
}
5. 实际应用场景
重载的应用
// 1. 提供多种调用方式
public class StringUtils {
public static boolean isEmpty(String str) {
return str == null || str.length() == 0;
}
public static boolean isEmpty(String str, boolean trim) {
return trim ? isEmpty(str.trim()) : isEmpty(str);
}
}
// 2. 类型转换的便利性
public class DataConverter {
public String convert(int value) {
return String.valueOf(value);
}
public String convert(double value) {
return String.valueOf(value);
}
public String convert(Object obj) {
return obj.toString();
}
}
重写的应用
// 1. 模板方法模式
public abstract class AbstractProcessor {
public final void process() {
preProcess();
doProcess(); // 子类重写
postProcess();
}
protected abstract void doProcess();
protected void preProcess() {}
protected void postProcess() {}
}
// 2. 策略模式
public interface PaymentStrategy {
void pay(double amount);
}
public class AlipayStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount);
}
}
public class WechatStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount);
}
}
答题总结
面试要点:
- 重载:同一类中方法名相同、参数不同,编译期决定调用哪个方法(静态多态)
- 重写:子类重新定义父类方法,运行期根据对象实际类型决定调用哪个方法(动态多态)
- 记忆口诀:
- 重载看参数,编译期决定
- 重写看对象,运行期决定
- 最佳实践:
- 重写方法必须加
@Override注解,避免拼写错误 - 重载方法参数差异要明显,避免调用歧义
- 重写时遵守里氏替换原则,子类行为应符合父类契约
- 重写方法必须加